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