Same type but different lifetime

Hi,

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>.

struct Builder<T> {
    t: std::marker::PhantomData<*const T>,
}
impl<T> Builder<T> {
    fn new() -> Self {
        Self {
            t: std::marker::PhantomData,
        }
    }
    fn process(&mut self, _x: T) {}
}
struct U<'a>(&'a String);

fn main() {
    let mut builder = Builder::new();
    for _ in 0..10 {
        let x = "test".to_string();
        builder.process(U(&x));
    }
    drop(builder);
}

The above does not compile, as the x do not live long enough.

Is there any way to make this idea work?
One would like to have process accept "T but with any lifetime", say

fn process<'a>(&mut self, _x: T<'a>) {}

but this is not valid syntax for generic T, only for a give struct T<'a>

Maybe like this?

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
    }

Unfortunately, it won't compile Rust Playground

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<'_> {}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.