Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

rust

lifetime

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

like image 693
AlphaModder Avatar asked May 26 '18 23:05

AlphaModder


People also ask

How does lifetime work in Rust?

Lifetimes are what the Rust compiler uses to keep track of how long references are valid for. Checking references is one of the borrow checker's main responsibilities. Lifetimes help the borrow checker ensure that you never have invalid references.

What is a lifetime parameter in Rust?

Rust uses lifetime parameters to avoid such potential run-time errors. Since the compiler doesn't know in advance whether the if or the else block will execute, the code above won't compile and an error message will be printed that says, “a lifetime parameter is expected in compare 's signature.”

What is Anonymous lifetime Rust?

'_ , the anonymous lifetime Rust 2018 allows you to explicitly mark where a lifetime is elided, for types where this elision might otherwise be unclear. To do this, you can use the special lifetime '_ much like you can explicitly mark that a type is inferred with the syntax let x: _ = ..; .

What are Trait bounds in Rust?

Trait and lifetime bounds provide a way for generic items to restrict which types and lifetimes are used as their parameters. Bounds can be provided on any type in a where clause.


1 Answers

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.

like image 114
Shepmaster Avatar answered Oct 20 '22 01:10

Shepmaster