Lifetime of Struct used in GenericType

Hi,

I am writing a program that allow users to define arbitrary mathematical functions to compute some values. But I have an issue of specifying lifetime for struct that is used as generic type. Here is a minimal example that demonstrate it, so overall, I have models, which have list of functions, and a function can compute value from variables

pub trait Function<V> {
    fn exec(&self, var: &V);
}

pub struct Variable<'a> {
    name: &'a str,
}

pub struct FunctionImpl {
}

impl<'a> Function<Variable<'a>> for FunctionImpl {
    fn exec(&self, var: &Variable<'a>) {
        println!("exec func");
    }
}

pub struct Model<V> {
    funcs: Vec<Box<Function<V>>>,
}

impl<V> Model<V> {
    pub fn new(funcs: Vec<Box<Function<V>>>) -> Model<V> {
        Model { funcs }
    }

    pub fn exec(&self, var: &V) {
        println!("exec model");
    }
}

The problem here is sometime structs implement V (like Variable struct) can contain references. Which make this code unable to compile.

fn main() {
    let model = Model::new(vec![Box::new(FunctionImpl {})]);
    {
        let example = Variable {
            name: "someone",
        };
        model.exec(&example);
    }

    {
        let x = "someoneelse".to_owned();
        let example = Variable {
            name: &x,
        };
        model.exec(&example);
    }
}

Error:


59 |             name: &x,
   |                    ^ borrowed value does not live long enough
...
62 |     }
   |     - `x` dropped here while still borrowed
...
65 | }
   | - borrowed value needs to live until here

Which I think life time 'a of trait Function bounded outside, so it is outlive lifetime of x. However, since I cannot store any value of V inside Function (func exec is immutable), and model; I was hope that Rust can detect that lifetime 'a only bound to the function exec, and can cast life time 'a to the shorter lifetime of x.

Also, I found this code can compile well.

fn main() {
  let func = FunctionImpl {};
  {
        let example = Variable {
            name: "someone",
        };
        func.exec(&example);
    }

    {
        let x = "someoneelse".to_owned();
        let example = Variable {
            name: &x,
        };
        func.exec(&example);
    }
}

I got one solution that use Rc/Arc for reference, so I don’t need lifetime. However, I want to avoid it because I may have to deal with creating millions of variables, so Rc/Arc can be costly.

My question is: how can I overcome this problem? Is Rust actually correct to prevent possible error with this design?

Thank you for your time! Any help is greatly appreciate!

Are you certain that you have to use temporary str reference instead of an owned String?

I see you use it with a string literal. If you only ever want literals, then remove the lifetime from the struct and use &'static str. Otherwise don’t put references in structs. Use owned types there — String in this case.

Thanks for your reply. &str is for demonstration purpose, it keeps the example as minimal as possible. In my app, it is a reference to a graph of thousand nodes.

I think this has to do with trait objects being invariant over their type parameters (or associated types).

In short, the compiler is not able to infer the variance of V in Function<V> when it’s a trait object. As such, it can’t rule out that the V argument may get stored internally, and if V doesn’t outlive the Model (like your second example), it would lead to unsoundness; as such it wants all &str to outlive model.

What you’d likely want to say is Box<for<'a> Function<V<'a>>>> but that requires V to be a concrete type (in today’s Rust, at least).

I’d be happy if someone can correct my understanding or elaborate on it.

1 Like