The aim of the following snippet (playground) is to construct a Builder object that can take objects of type T and process them in a way that does not make it depend on lifetimes of T.
An example would be copying T: serde::Deserialize<'a> to a Vec<serde_json::Value>.
What's the type of the value builder? This must be clear. T is determined by the process method which imposes the lifetime from the value inside the for block:
let mut builder = Builder::new(); // builder: Builder<U<'lifetime>>
for _ in 0..10 {
let x = "test".to_string();
builder.process(U(&x)); // where 'lifetime derives from &'lifetime x
// but x drops at the end of for block, meaning the other x can't live for the same 'lifetime
}
this is shown by the compiler error msg
error[E0597]: `x` does not live long enough
--> src/main.rs:18:27
|
17 | let x = "test".to_string();
| - binding `x` declared here
18 | builder.process(U(&x));
| ^^ borrowed value does not live long enough
19 | }
| - `x` dropped here while still borrowed
20 | drop(builder);
| ------- borrow later used here
So how to solve it? It depends. You can use a buffer outside the for block and always refer to the buffer:
let mut builder = Builder::new();
let mut buffer = String::new();
for _ in 0..10 {
let mut x = "test".to_string(); // the value is generated here
std::mem::swap(&mut buffer, &mut x); // swap the values
builder.process(U(&buffer)); // use the reference to the buffer instead of inner block
}
error[E0502]: cannot borrow `buffer` as mutable because it is also borrowed as immutable
--> src/main.rs:19:24
|
19 | std::mem::swap(&mut buffer, &mut x); // swap the values
| ^^^^^^^^^^^ mutable borrow occurs here
20 | builder.process(U(&buffer)); // use the reference to the buffer instead of inner block
| ---------------------------
| | |
| | immutable borrow occurs here
| immutable borrow later used here
Then you can resort to the interior mutability and it works Rust Playground
let buffer = RefCell::new(String::new());
for i in 0..10 {
let mut x = format!("test: {i}"); // the value is generated here
std::mem::swap(&mut *buffer.borrow_mut(), &mut x); // swap the values
builder.process(U(&buffer)); // use the reference to the buffer instead of inner block
}
as pointed out by vague, the type Builder<T> is parameterized by T, but from the example use case of conversion from Deserialize<'de> to serde_json::Value, it seems to me what you want is the Builder::process() method should be parameterized instead. so maybe you can change your design, e.g. make Builder non generic, or if the Builder type need to be generic, it can be parameterized by different type from the Builder::process() method, something like:
struct Builder <T> {
_marker: PhantomData<Fn(T) -> T>, // depending what variant you want Builder to be over T
}
/// trait bound for parameter U, depeding your use case, you might not need
/// to define your own trait and just use external traits, e.g. `U: Deserialize<'_>`
/// if doesn't need to be parameterized by T, here I'm just giving an example.
/// however, you might need to annotate the type T when calling `Builder::new()`
/// if the type T cannot be inferred from U.
trait ProcessParameter<T> {}
impl<T> Builder<T> {
fn process<U>(&mut self, _x: U) where U: ProcessParameter<T> {
// do your work use the trait `ProcessParameter<T>`
}
}
struct U<'a>(&'a String);
impl ProcessParameter<String> for U<'_> {}