Stuck with getting lifetime right

I have a struct, that contains a vec of enums.
Some of the enums have references in them
the struct impl supports a trait overloaded method for adding to that vec for all sorts of different types.

struct:

pub struct FuncDef<'argval> {
    ...
    pub(crate) arg_vals: Vec<ArgVal<'argval>>,
}

enum:

pub enum ArgVal<'argval> {
    Pointer(*mut c_void),
    U64(u64),
    F64(f64),
    I64(i64),
    I32(i32),
    U32(u32),
    I16(i16),
    U16(u16),
    F32(f32),
    Char(u8),
    CString(CString),
    RustString(&'argval String),  <<< note lifetimed ref
    None,
}

trait

pub trait ToMutArg<'argval> {
    fn to_mut_arg(&mut self, func: &mut FuncDef<'argval>) -> *mut c_void;
}

generic push using that trait

impl<'argval> FuncDef<'argval> {

    pub fn push_mut_arg<T>(&mut self, value: &mut T)
    where
        T: ToMutArg<'argval> + ?Sized,
    {
        let argp = value.to_mut_arg(self);
        self.arg_ptrs.push(argp);
    }

trait impl for String (my first one to implement)

impl<'argval> ToMutArg<'argval> for String {
    fn to_mut_arg(&mut self, func: &mut FuncDef<'argval>) -> *mut c_void {
        let arg_idx = func.arg_ptrs.len();
        let arg_type = &func.arg_types[arg_idx];

        match arg_type {
            ArgType::OCString => {
                let mut buffer = self.as_mut_ptr() as *mut c_void;
                func.arg_vals.push(ArgVal::Pointer(buffer));

                func.arg_vals.push(ArgVal::RustString(self));

produces

error: lifetime may not live long enough
  --> src\args.rs:91:17
   |
81 | impl<'argval> ToMutArg<'argval> for String {
   |      ------- lifetime `'argval` defined here
82 |     fn to_mut_arg(&mut self, func: &mut FuncDef<'argval>) -> *mut c_void {
   |                   - let's call the lifetime of this reference `'1`
...
91 |                 func.arg_vals.push(ArgVal::RustString(self));
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'argval`
   |

I thought I had understood lifetimes, clearly not well enough

I think this change should make the code compile:

pub trait ToMutArg<'argval> {
-    fn to_mut_arg(&mut self, func: &mut FuncDef<'argval>) -> *mut c_void;
+    fn to_mut_arg(&'argval mut self, func: &mut FuncDef<'argval>) -> *mut c_void;
}

the &mut T reference is invaraint over the type T, e.g. in func: &mut FuncDeref<'argval>.

anyways, mut references can be hard to work with. although you might be able to make this piece of code compile by chaning the trait method's signature, be aware you might encounter other subtle lifetime issues related to variance.

1 Like

that was it - ty