Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define a trait with a function that returns an associated type with the same lifetime as one parameter

I'm trying to define a trait with a function that returns an associated type with the same lifetime as one parameter.

Conceptually something like the following (which doesn't work: lifetime parameter not allowed on this type [Self::Output]):

trait Trait {
    type Output;
    fn get_output<'a>(&self, input: &'a i32) -> Self::Output<'a>;
}

I found several questions about lifetimes for associated types on Stack Overflow and the Internet, but none seem to help. Some suggested defining the lifetime on the whole trait:

trait Trait<'a> {
    type Output;
    fn get_output(&self, input: &'a i32) -> Self::Output;
}

but this doesn't work either: it compiles, but then the following function fails to compile:

fn f<'a, T>(var: &T)
    where T: Trait<'a>
{
    let input = 0i32;
    let output = var.get_output(&input);
}

giving an error:

error: `input` does not live long enough
  --> <anon>:9:35
   |
   |     let output = var.get_output( &input );
   |                                   ^^^^^ does not live long enough
   | }
   | - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the body at 7:48...
  --> <anon>:7:49
   |
   |   fn f<'a, T>( var : &T ) where T : Trait<'a> {
   |  _________________________________________________^ starting here...
   | |     let input = 0i32;
   | |     let output = var.get_output( &input );
   | | }
   | |_^ ...ending here

How should I define the trait so that it behaves the way I want?

like image 841
peoro Avatar asked Mar 09 '23 17:03

peoro


2 Answers

This is currently impossible, even in nightly Rust.

This requires some form of Higher Kinded Types (HKT), and the current approach envisaged is dubbed Associated Type Constructor (ATC).

The main motivation for introducing ATC is actually this very usecase.

With ATC, the syntax would be:

trait Trait {
    type Output<'a>;
    fn get_output<'a>(&self, input: &'a i32) -> Self::Output<'a>;
}

Note: if you wish to learn about ATCs, see Niko's series of articles.


For the particular example you have, you can work around this with HRTB (Higher Ranked Traits Bounds) as noted by Shepmaster:

fn f<T>(var: &T)
    where for<'a> T: Trait<'a>
{ ... }

however this is fairly limited (notably, limited to trait bounds).

like image 119
Matthieu M. Avatar answered Apr 20 '23 00:04

Matthieu M.


This requires using higher-ranked trait bounds:

fn f<T>(var: &T)
    where for<'a> T: Trait<'a>
{
    let input = 0i32;
    let output = var.get_output(&input);
}

See also:

  • How does for<> syntax differ from a regular lifetime bound?
like image 20
Shepmaster Avatar answered Apr 20 '23 00:04

Shepmaster