Storing a closure with lifetimes in a struct - rust

I'm trying to store closures in a Vec that is part of a struct. The closure is a factory function which receives 2 references as arguments and produces a trait object which stores the references the closure receives as arguments.
Because of that, the produced trait object has a lifetime that must not exceed the lifetime of the references. Also component_registrations will be accessed from multiple threads and is therefore wrapped in an Arc<Mutex>.
I tried implementing it but the compiler says that the generic parameter F of the register_component function doesn't satisfy the trait bound used in component_registrations.
This is the relevant part of the code:
use std::sync::Mutex;
use std::sync::Arc;
pub mod gl {
pub struct Gl();
}
pub struct ComponentRegistry<'a> {
gl: &'a gl::Gl
}
pub trait Component<'a> {
}
pub struct Application {
component_registrations: Arc<Mutex<Vec<Box<for<'b> Fn(&'b ComponentRegistry<'b>, &'b gl::Gl) -> Box<Component<'b>> + Send + 'static>>>>
}
impl Application {
pub fn new() -> Application {
Application {
component_registrations: Arc::new(Mutex::new(vec![]))
}
}
pub fn register_component<'a, F>(&mut self, register: F) where F: Fn(&'a ComponentRegistry<'a>, &'a gl::Gl) -> Box<Component<'a>> + Send + 'static {
self.component_registrations.lock().unwrap().push(Box::new(register));
}
}
error[E0277]: the trait bound `for<'b> F: std::ops::Fn<(&'b ComponentRegistry<'b>, &'b gl::Gl)>` is not satisfied
--> src/main.rs:27:59
|
27 | self.component_registrations.lock().unwrap().push(Box::new(register));
| ^^^^^^^^^^^^^^^^^^ the trait `for<'b> std::ops::Fn<(&'b ComponentRegistry<'b>, &'b gl::Gl)>` is not implemented for `F`
|
= help: consider adding a `where for<'b> F: std::ops::Fn<(&'b ComponentRegistry<'b>, &'b gl::Gl)>` bound
= note: required for the cast to the object type `for<'b> std::ops::Fn(&'b ComponentRegistry<'b>, &'b gl::Gl) -> std::boxed::Box<Component<'b>> + std::marker::Send`
error[E0271]: type mismatch resolving `for<'b> <F as std::ops::FnOnce<(&'b ComponentRegistry<'b>, &'b gl::Gl)>>::Output == std::boxed::Box<Component<'b>>`
--> src/main.rs:27:59
|
27 | self.component_registrations.lock().unwrap().push(Box::new(register));
| ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'b, found concrete lifetime
|
note: concrete lifetime that was found is the lifetime 'a as defined on the method body at 26:5
--> src/main.rs:26:5
|
26 | / pub fn register_component<'a, F>(&mut self, register: F) where F: Fn(&'a ComponentRegistry<'a>, &'a gl::Gl) -> Box<Component<'a>> + Send + 'static {
27 | | self.component_registrations.lock().unwrap().push(Box::new(register));
28 | | }
| |_____^
= note: required for the cast to the object type `for<'b> std::ops::Fn(&'b ComponentRegistry<'b>, &'b gl::Gl) -> std::boxed::Box<Component<'b>> + std::marker::Send`

If you use a higher ranked lifetime when you define your component_registrations struct field, you should use a higher ranked lifetime for F as well.
Also, if you say Box<Component<'b>>, it really means Box<Component<'b> + 'static> (so the trait object can contain only owned data). What you really need is Box<Component<'b> + 'b>, which means it is a trait object that implements Component<'b> and it can also contain borrowed data which live at least as long as 'b.
The relevant part is
pub struct Application {
component_registrations: Vec<Box<for<'b> Fn(&'b ComponentRegistry<'b>, &'b gl::Gl) -> Box<Component<'b> + 'b> + Send + 'static>>
}
impl Application {
pub fn register_component<F>(&mut self, register: F) where F: for<'b> Fn(&'b ComponentRegistry<'b>, &'b gl::Gl) -> Box<Component<'b> + 'b> + Send + 'static {
self.component_registrations.push(Box::new(register));
}
}
You can see the full example. Note, that I removed the Arc and Mutex types from your example since they were not relevant.

Related

Lifetime collision when bounding reference of a trait as IntoIterator

I tried to implement some graph algorithms on generic graphs. For that, I defined two graph traits which would return either a generic trait (having set-operations) SetGraph or an IntoIterator used to iterate over the nodes NeighborhoodIteratorGraph.
pub trait NeighborhoodIteratorGraph<'a> {
//which into_iterator do we have?
type IntoIter: 'a + std::iter::IntoIterator<Item = usize>;
fn get_neighborhood_iterator(&'a self, index: usize) -> Self::IntoIter;
}
pub trait SetGraph<'a>
where
&'a Self::S: IntoIterator<Item = usize>,
Self::S: 'a,
{
type S;
fn get_neighborhood(&'a self, index: usize) -> &'a Self::S;
}
Because one is usually able to iterate over sets, I also implemented NeighborhoodIteratorGraph for all SetGraph which are able to iterate over their sets.
impl<'a, G> NeighborhoodIteratorGraph<'a> for G
where
G: SetGraph<'a>,
&'a G::S: IntoIterator<Item = usize>,
{
type IntoIter = &'a G::S;
fn get_neighborhood_iterator(&'a self, index: usize) -> Self::IntoIter {
self.get_neighborhood(index)
}
}
I needed to add a lifetime to NeighborrhoodIteratorGraph otherwise the compiler would tell me my implementation would have an unbounded lifetime.
However I quicky run into problems with these lifetimes and I get an error for the following code:
struct Foo<'a, G: NeighborhoodIteratorGraph<'a>> {
graph: G,
//otherwise we get an error because 'a wouldn't be used
_marker: std::marker::PhantomData<&'a G>,
}
impl<'a, G: NeighborhoodIteratorGraph<'a>> Foo<'a, G> {
pub fn find_matching_for<I>(&mut self, nodes: I) -> bool
where
I: std::iter::IntoIterator<Item = usize>,
{
for node in self.graph.get_neighborhood_iterator(3) {}
return true;
}
}
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
It seems that the PhantomData field is more a hack and I can't find a way in which I get a set refernce which can be seen as a IntoIterator object.
Here is the Rust Playground of the problem.
Full error message:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/lib.rs:38:32
|
38 | for node in self.graph.get_neighborhood_iterator(3) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 34:5...
--> src/lib.rs:34:5
|
34 | / pub fn find_matching_for<I>(&mut self, nodes: I) -> bool
35 | | where
36 | | I: std::iter::IntoIterator<Item = usize>,
| |_________________________________________________^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:38:21
|
38 | for node in self.graph.get_neighborhood_iterator(3) {}
| ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 33:6...
--> src/lib.rs:33:6
|
33 | impl<'a, G: NeighborhoodIteratorGraph<'a>> Foo<'a, G> {
| ^^
note: ...so that the types are compatible
--> src/lib.rs:38:32
|
38 | for node in self.graph.get_neighborhood_iterator(3) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `&'a G`
found `&G`
What you want is a workaround for the lack of generic associated types, which are currently very unstable. Something Like
pub trait NeighborhoodIteratorGraph {
type IntoIter<'a>: std::iter::IntoIterator<Item = usize> + 'a;
fn get_neighborhood_iterator<'b>(&'b self, index: usize) -> Self::IntoIter<'b>;
}
would serve you perfectly if they were stable.
The first thing I did is remove the lifetime bound on NeighborhoodIteratorGraph and add it to the return type:
pub trait NeighborhoodIteratorGraph {
type IntoIter: std::iter::IntoIterator<Item = usize>;
fn get_neighborhood_iterator<'b>(&'b self, index: usize) -> Self::IntoIter
where
Self::IntoIter: 'b;
}
I then removed unnecessary lifetime annotations from SetGraph:
pub trait SetGraph<'a>
where
&'a Self::S: IntoIterator<Item = usize>,
Self::S: 'a,
{
type S;
fn get_neighborhood(&self, index: usize) -> &Self::S;
}
I then changed the blanket impl's signature to match the modified traits, and changed the impl from G to &'a G to properly constrain the lifetime 'a:
impl<'a, G> NeighborhoodIteratorGraph for &'a G
where
G: SetGraph<'a>,
&'a G::S: IntoIterator<Item = usize>,
{
type IntoIter = &'a G::S;
fn get_neighborhood_iterator<'b>(&'b self, index: usize) -> Self::IntoIter
where
Self::IntoIter: 'b,
{
self.get_neighborhood(index)
}
}
Because of those changes I was able to simplify Foo and its impl:
struct Foo<G: NeighborhoodIteratorGraph> {
graph: G,
}
impl<G: NeighborhoodIteratorGraph> Foo<G> {
pub fn find_matching_for<I>(&mut self, nodes: I) -> bool
where
I: std::iter::IntoIterator<Item = usize>,
{
for node in self.graph.get_neighborhood_iterator(3) {}
return true;
}
}
Leaving the compiler output with nothing but dead code warnings. Playground link

Unexpected error when using explicit lifetime in template argument in Rust [duplicate]

I have a trait Matrix and generic function semi_def<T: Matrix>(x: &T) that I would like to operate on that trait. The function requires an operator trait, say Mul, be implemented on T. However, I can't seem to make the lifetimes happy if one of the references is to a local variable. How do I write the lifetimes for references in the type constraint when one of them is just a local temporary reference?
use std::ops::Mul;
trait Matrix: Clone {
fn transpose(self) -> Self;
}
#[derive(Clone)]
struct DenseMatrix {
n_rows: usize,
n_columns: usize,
elements: Vec<f64>,
}
impl Matrix for DenseMatrix {
fn transpose(self) -> Self {
unimplemented!()
}
}
impl<'a, 'b> Mul<&'b DenseMatrix> for &'a DenseMatrix {
type Output = DenseMatrix;
fn mul(self, _rhs: &'b DenseMatrix) -> Self::Output {
unimplemented!()
}
}
fn semi_def<'a, T: Matrix>(x: &'a T) -> T
where
&'a T: Mul<&'a T, Output = T>,
{
&(*x).clone().transpose() * x
}
fn main() {}
which gives this error:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:31:6
|
31 | &(*x).clone().transpose() * x
| ^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
32 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 27:1...
--> src/main.rs:27:1
|
27 | / fn semi_def<'a, T: Matrix>(x: &'a T) -> T
28 | | where
29 | | &'a T: Mul<&'a T, Output = T>,
30 | | {
31 | | &(*x).clone().transpose() * x
32 | | }
| |_^
You need higher-ranked trait bounds (HRTBs), which are described in the advanced Rust book Rustonomicon and well as on Stack Overflow. They allow a type constraint to say that trait must be implemented not just for references with a particular lifetime but for any lifetime. They use the where for<> syntax. Here is the function definition that says an implementation of Mul is needed for any two references to T:
fn semi_def<'a, T: Matrix>(x: &'a T) -> T
where
for<'b, 'c> &'b T: Mul<&'c T, Output = T>,
{
&(*x).clone().transpose() * x
}
Because one of the references actually has the lifetime 'a, not a local lifetime, this could be written with a slightly looser constraint:
fn semi_def<'a, T: Matrix>(x: &'a T) -> T
where
for<'b> &'b T: Mul<&'a T, Output = T>,
{
&(*x).clone().transpose() * x
}
This Q&A is based off a question I asked on the Rust users mailing, which I cleaned up and brought over here for future Rustaceans.

How to specify that a lifetime from an impl should be the same as the lifetime of 'self'?

I'm starting from a root Fmt and descending recursively (along a Vec<String>) into Fmts produced by a get_subfmt call. When the vector is empty, I call a method (not shown here for brevity). Each preceding Fmt (and its BoxOrRef) has naturally greater lifetime enforced by the recursive function's scope than the following one. This seems safe, but I'm very new to the lifetimes business, so I may have erred in my reasoning somewhere.
Consider the following bit of simplified code:
use std::borrow::Borrow;
pub trait Fmt {
fn get_subfmt<'a>(&'a self, name: &str) -> Option<BoxOrRef<'a, dyn Fmt>>;
}
pub enum BoxOrRef<'a, T: ?Sized + 'a> {
Boxed(Box<T>),
Ref(&'a T)
}
impl<'b, T: Borrow<dyn Fmt + 'b>> Fmt for T {
fn get_subfmt(&'b self, name: &str) -> Option<BoxOrRef<'b, dyn Fmt>> {
self.borrow().get_subfmt(name)
}
}
This fails with the following errors:
error[E0308]: method not compatible with trait
--> src/lib.rs:13:5
|
13 | fn get_subfmt(&'b self, name: &str) -> Option<BoxOrRef<'b, dyn Fmt>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected type `fn(&'a T, &str) -> std::option::Option<BoxOrRef<'a, Fmt + 'a>>`
found type `fn(&'b T, &str) -> std::option::Option<BoxOrRef<'b, Fmt + 'b>>`
note: the lifetime 'a as defined on the method body at 13:5...
--> src/lib.rs:13:5
|
13 | fn get_subfmt(&'b self, name: &str) -> Option<BoxOrRef<'b, dyn Fmt>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime 'b as defined on the impl at 12:1
--> src/lib.rs:12:1
|
12 | impl<'b, T: Borrow<dyn Fmt + 'b>> Fmt for T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0308]: method not compatible with trait
--> src/lib.rs:13:5
|
13 | fn get_subfmt(&'b self, name: &str) -> Option<BoxOrRef<'b, dyn Fmt>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected type `fn(&'a T, &str) -> std::option::Option<BoxOrRef<'a, Fmt + 'a>>`
found type `fn(&'b T, &str) -> std::option::Option<BoxOrRef<'b, Fmt + 'b>>`
note: the lifetime 'b as defined on the impl at 12:1...
--> src/lib.rs:12:1
|
12 | impl<'b, T: Borrow<dyn Fmt + 'b>> Fmt for T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime 'a as defined on the method body at 13:5
--> src/lib.rs:13:5
|
13 | fn get_subfmt(&'b self, name: &str) -> Option<BoxOrRef<'b, dyn Fmt>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It seems that the compiler can't prove that the 'a from the definition of get_subfmt is the same as 'b from the impl, most likely because I haven't told it that it is. How do I communicate to the compiler that I want the self reference to last precisely 'b? I can't slap a <'a: 'b> on the get_subfmt in the impl, as that doesn't match the method signature. I've tried constraining T to be T: Borrow<dyn Fmt + 'b> + 'b, but that doesn't help, the errors remain.
I could move 'a from the method to the Fmt itself, but it doesn't really seem right, so I would like to avoid it if there's a way to.
This is the best I could come up with:
use std::borrow::Borrow;
pub trait Fmt<'b> {
fn get_subfmt<'a>(&'a self, name: &str) -> Option<BoxOrRef<'a, dyn Fmt + 'a>>
where
'b: 'a;
}
pub enum BoxOrRef<'a, T: ?Sized + 'a> {
Boxed(Box<T>),
Ref(&'a T)
}
impl<'b, T: Borrow<dyn Fmt<'b> + 'b>> Fmt<'b> for T {
fn get_subfmt<'a>(&'a self, name: &str) -> Option<BoxOrRef<'a, dyn Fmt + 'a>>
where
'b: 'a,
{
self.borrow().get_subfmt(name)
}
}
We want the bound 'b: 'a ('b outlives 'a), not 'a: 'b, because a borrowed reference cannot outlive its referent.

A smart constructor for an iterator with a reference to a closure inside

Consider the following code for a (greatly simplified) iterator with a reference to a closure inside:
struct IteratorState<'a, T: 'a + Fn(i32) -> i32> {
closure: &'a T,
}
impl<'a, T: 'a + Fn(i32) -> i32> Iterator for IteratorState<'a, T> {
type Item = i32;
fn next(&mut self) -> Option<i32> {
None
}
}
It compiles and I can construct IteratorStates directly. However, I also need a smart constructor to hide some details of the implementation (not shown in the MCVE). The following attempt does not compile:
fn mk_iter<'a, T: Fn(i32) -> i32>(closure: &'a T) -> impl Iterator<Item = i32> {
IteratorState { closure }
}
The error is
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
--> src/lib.rs:14:5
|
14 | IteratorState { closure }
| ^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime 'a as defined on the function body at 13:1...
--> src/lib.rs:13:1
|
13 | fn mk_iter<'a, T: Fn(i32) -> i32>(closure: &'a T) -> impl Iterator<Item = i32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:14:21
|
14 | IteratorState { closure }
| ^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that return value is valid for the call
--> src/lib.rs:13:54
|
13 | fn mk_iter<'a, T: Fn(i32) -> i32>(closure: &'a T) -> impl Iterator<Item = i32> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
I think I understand what the problem is: there's no guarantee that constructed IteratorState won't outlive the contained reference (please correct me if I got this wrong), but I'm not quite sure how to fix it.
The impl Trait syntax supports adding lifetimes to the return type:
fn mk_iter<'a, T: Fn(i32) -> i32>(closure: &'a T) -> impl Iterator<Item = i32> + 'a {
// here ^^^^
IteratorState {
closure
}
}
(link to playground)

How can I get impl Trait to use the appropriate lifetime for a mutable reference to a value with another lifetime in it?

I have a struct with a lifetime:
struct HasLifetime<'a>( /* ... */ );
There is there is an implementation of the trait Foo:
impl<'a, 'b: 'a> Foo for &'a mut HasLifetime<'b> { }
I want to implement the following function:
fn bar_to_foo<'a, 'b: 'a>(bar: &'a mut Lifetime<'b>) -> impl Foo {
bar
}
This won't compile because the returned impl is only valid for 'a. However, specifying impl Foo + 'a results in:
error[E0909]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
--> src/main.rs:7:60
|
7 | fn bar_to_foo<'a, 'b: 'a>(bar: &'a mut HasLifetime<'b>) -> impl Trait + 'a {
| ^^^^^^^^^^^^^^^
|
note: hidden type `&'a mut HasLifetime<'b>` captures the lifetime 'b as defined on the function body at 7:1
--> src/main.rs:7:1
|
7 | fn bar_to_foo<'a, 'b: 'a>(bar: &'a mut HasLifetime<'b>) -> impl Trait + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The seemingly equivalent function with a boxed trait object compiles:
fn bar_to_foo<'a, 'b: 'a>(bar: &'a mut Lifetime<'b>) -> Box<Foo + 'a> {
Box::new(bar)
}
How can I define bar_to_foo with impl Trait?
Playground link
You need to indicate that the returned value is built upon multiple lifetimes. However, you can't use multiple lifetime bounds with impl Trait, and attempting to do so doesn't have a useful error message.
There's a trick you can use that involves creating a dummy trait that has a lifetime parameter:
trait Captures<'a> {}
impl<'a, T: ?Sized> Captures<'a> for T {}
fn bar_to_foo<'a, 'b: 'a>(bar: &'a mut HasLifetime<'b>) -> impl Trait + Captures<'b> + 'a {
bar
}
Thankfully, this only occurs when the "hidden" lifetime is invariant, which occurs because the reference is mutable.

Resources