2
votes

I am having trouble figuring out what lifetime parameter will work for this, so my current workarounds include transmutes or raw pointers. I have a structure holding a function pointer with a generic as a parameter:

struct CB<Data> {
    cb: fn(Data) -> usize
}

I would like to store an instance of that, parameterized by some type containing a reference, in some other structure that implements a trait with one method, and use that trait method to call the function pointer in CB.

struct Holder<'a> {
    c: CB<Option<&'a usize>>
}
trait Exec {
    fn exec(&self, v: &usize) -> usize;
}
impl<'a> Holder<'a> {
    fn exec_aux(&self, v: &'a usize) -> usize {
        (self.c.cb)(Some(v))
    }
}
impl<'a> Exec for Holder<'a> {
    fn exec(&self, v: &usize) -> usize
    {
        self.exec_aux(v)
    }
}

This gives me a lifetime error for the 'Exec' impl of Holder:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements

Simply calling exec_aux works fine as long as I don't define that Exec impl:

fn main() {
    let h = Holder { c: CB{cb:cbf}};
    let v = 12;
    println!("{}", h.exec_aux(&v));
}

Also, making CB not generic also makes this work:

struct CB {
    cb: fn(Option<&usize>) -> usize
}

The parameter in my actual code is not a usize but something big that I would rather not copy.

2

2 Answers

2
votes

The lifetimes in your Exec trait are implicitly this:

trait Exec {
    fn exec<'s, 'a>(&'s self, v: &'a usize) -> usize;
}

In other words, types that implement Exec need to accept any lifetimes 's and 'a. However, your Holder::exec_aux method expects a specific lifetime 'a that's tied to the lifetime parameter of the Holder type.

To make this work, you need to add 'a as a lifetime parameter to the Exec trait instead, so that you can implement the trait specifically for that lifetime:

trait Exec<'a> {
//        ^^^^         vv
    fn exec(&self, v: &'a usize) -> usize;
}

impl<'a> Exec<'a> for Holder<'a> {
//           ^^^^      vv
    fn exec(&self, v: &'a usize) -> usize
    {
        self.exec_aux(v)
    }
}
1
votes

The problem here is that the Exec trait is too generic to be used in this way by Holder. First, consider the definition:

trait Exec {
    fn exec(&self, v: &usize) -> usize;
}

This definition will cause the compiler to automatically assign two anonymous lifetimes for &self and &v in exec. It's basically the same as

fn exec<'a, 'b>(&'a self, v: &'b usize) -> usize;

Note that there is no restriction on who needs to outlive whom, the references just need to be alive for the duration of the method call.

Now consider the definition

impl<'a> Holder<'a> {
    fn exec_aux(&self, v: &'a usize) -> usize {
        // ... doesn't matter
    }
}

Since we know that &self is a &Holder<'a> (this is what the impl refers to), we need to have at least a &'a Holder<'a> here, because &'_ self can't have a lifetime shorter than 'a in Holder<'a>. So this is saying that the two parameters have the same lifetime: &'a self, &'a usize.

Where it all goes wrong is when you try to combine the two. The trait forces you into the following signature, which (again) has two distinct implicit lifetimes. But the actual Holder which you then try to call a method on forces you to have the same lifetimes for &self and &v.

fn exec(&self, v: &usize) -> usize {
    // Holder<'a> needs `v` to be `'a` when calling exec_aux
    // But the trait doesn't say so.
    self.exec_aux(v)
}

One solution is to redefine the trait as

trait Exec<'a> {
    fn exec(&'a self, v: &'a usize) -> usize;
}

and then implement it as

impl<'a> Exec<'a> for Holder<'a> {
    fn exec(&'a self, v: &'a usize) -> usize {
        self.exec_aux(v)
    }
}