-
-
Notifications
You must be signed in to change notification settings - Fork 110
Description
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?