Reputation: 6779
I tried to make a short reproducible example but couldn't reproduce my problem, so I stripped most of the contents from my main code retaining the error.
lib.rs
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{quote};
use syn::{
parse_macro_input, Data, DeriveInput, Field, Fields, Ident,
Type, TypeGenerics,
};
struct IterMapped<Ret, T: Iterator + Clone> {
iter: T,
map_func: Box<dyn FnMut(T::Item) -> Ret>,
}
impl<Ret, T: Iterator + Clone> IterMapped<Ret, T> {
pub fn new(iter: T, map_func: Box<dyn FnMut(T::Item) -> Ret>) -> Self {
IterMapped { iter, map_func }
}
}
enum BuilderField<'a> {
Optional(&'a Field, &'a Type),
Mandatory(&'a Field),
}
struct BuilderData<'a> {
struct_name: &'a Ident,
builder_struct_name: &'a Ident,
ty_generics: &'a TypeGenerics<'a>,
fields_mapped_iter: IterMapped<BuilderField<'a>, syn::punctuated::Iter<'a, Field>>,
}
#[proc_macro_derive(Builder)]
pub fn derive(input: TokenStream) -> TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput);
let data = &derive_input.data;
let struct_name = &derive_input.ident;
let (impl_generics, ty_generics_temp, where_clause) = derive_input.generics.split_for_impl();
let fields_iter = match data {
Data::Struct(ref data_struct) => match data_struct.fields {
Fields::Named(ref fields_named) => fields_named.named.iter(),
Fields::Unnamed(_) | Fields::Unit => {
panic!()
}
},
_ => panic!()
};
let fields_mapped_iter = IterMapped::new(
fields_iter,
Box::new(|field| BuilderField::Mandatory(field)),
);
let ref ty_generics = ty_generics_temp;
let ref builder_struct_name = Ident::new(
&format!("{}Builder", struct_name),
proc_macro2::Span::call_site(),
);
let mut builder_data = BuilderData {
struct_name,
builder_struct_name,
ty_generics,
fields_mapped_iter,
};
TokenStream::from(quote!())
}
Cargo.toml
[package]
name = "derive_builder"
version = "0.0.0"
edition = "2018"
autotests = false
publish = false
[lib]
proc-macro = true
[dependencies]
syn = "1.0.48"
quote = "1.0.7"
proc-macro2 = "1.0.24"
Running it produces the following error:
error[E0716]: temporary value dropped while borrowed
--> builder/src/lib.rs:58:35
|
58 | let ref builder_struct_name = Ident::new(
| ___________________________________^
59 | | &format!("{}Builder", struct_name),
60 | | proc_macro2::Span::call_site(),
61 | | );
| |_____^ creates a temporary which is freed while still in use
...
71 | }
| -
| |
| temporary value is freed at the end of this statement
| borrow might be used here, when `fields_mapped_iter` is dropped and runs the destructor for type `IterMapped<BuilderField<'_>, syn::punctuated::Iter<'_, syn::data::Field>>`
|
= note: consider using a `let` binding to create a longer lived value
I don't understand why dropping builder_struct_name
's temporary effects fields_mapped_iter
. Those two are separate fields.
If I change BuilderData
to use lifetime 'b
, the code compiles and works correctly:
struct BuilderData<'a, 'b> {
struct_name: &'a Ident,
builder_struct_name: &'a Ident,
ty_generics: &'a TypeGenerics<'a>,
fields_mapped_iter: IterMapped<BuilderField<'b>, syn::punctuated::Iter<'b, Field>>,
}
☝️Changing BuilderData
to this makes the code work.
If I rearrange the order in which the struct fields were declared and move let ref builder_struct_name = …
before setting of fields_iter
and fields_mapped_iter
it works because those 2 are then dropped before builder_struct_name
's temp. Why do their destructors rely on builder_struct_name
's temp?
Upvotes: 0
Views: 132
Reputation: 125925
When a scope ends, variables are dropped in reverse of the order in which they were declared. Hence BuilderData
's lifetime parameter 'a
cannot extend beyond when builder_struct_name
is dropped to the full life of fields_mapped_iter
. However, syn::punctuated::Iter
requires that its type argument T
outlives its
lifetime argument 'a
.
Upvotes: 1