Skip to content

Store Spanless Tokens as Strings #160

@rylev

Description

@rylev

Thanks for the awesome crate! 🎉 For the WinRT crate we generate a lot of code, and so performance is a concern. We noticed that when using the quote! macro a lot of destructors and clone implementations were being called. This is because the quote! macro conservatively borrows the variables it interpolates calling ToTokens::to_tokens on these variables which takes &self. This is presumably to support allowing interpolation of the same variable multiple times which definitely seems like a good default. However, this can be quite expensive to constantly clone and destroy the temporary variables that are only needed inside of the quote! macro. This might not be possible to avoid with some values like Strings, but we have many temporary TokenStreams where this cloning can be avoided.

As a workaround, I created a wrapper type which implements ToTokens and effectively moves the value it wraps into the resulting TokenStream like so:

use std::cell::RefCell;
use  proc_macro2::TokenStream; 
use quote::{quote, ToTokens}; 

struct TokenStreamWrapper(RefCell<Option<TokenStream>>);

impl TokenStreamWrapper {
    fn new(stream: TokenStream) -> Self {
        Self(RefCell::new(Some(stream)))
    }
}

impl ToTokens for TokenStreamWrapper {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let stream = self.0.replace(None).unwrap();
        tokens.extend(stream)
    }
}

This dramatically reduces the amount of times we run destructors. Is this the best way to tackle this issue? Would it be good for quote to have an API that allowed for this in cases where performance is needed?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions