0

I would like to create my own trait, which should yield a i32 based onindexing with a f32. I tried the following, which does seem to work:

use std::ops::Index;
// trait by value
pub trait MyTrait:
    Index<f32, Output=i32> {
}

While passing an f32 by value may be a good idea, for more complicated types I would rather pass a reference to the value instead, so I tried the following:

// trait by reference, not compiling
pub trait MyTrait:
    Index<&f32, Output=i32> {
}

Including this definition gives me an error[E0106]: missing lifetime specifier. I got this variant working:

// trait by reference, compiling
pub trait MyTrait<'a>:
    Index<&'a f32, Output=i32> {
}

Problem is: While this approach works, it means any type implementing MyTrait needs an explicit lifetime argument.

However, this seems rather unnecessary: If I implement the Index trait for my own struct I don't need any lifetimes:

struct Test {
  value: i32
}

impl Index<&f32> for Test {
  type Output = i32;

  fn index(&self, _key: &f32) -> &Self::Output {
    &self.value
  }
}

Question 1: Why do I need the additional lifetime in the definition of the trait, why can't it be elided?

Question 2: Can I define the trait in such a way as to avoid having to introduce the lifetime?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
hfhc2
  • 4,182
  • 2
  • 27
  • 56

2 Answers2

2

Why do I need the additional lifetime in the definition of the trait, why can't it be elided?

Lifetime elision is only applied to function signature. So no it won't work for your trait definition.

Can I define the trait in such a way as to avoid having to introduce the lifetime?

Certainly, you can use generic parameter just as what std::ops::Index does:

use std::ops::Index;
pub trait MyTrait<T>: Index<T, Output = i32> {}

struct Test {
    value: i32,
}

impl Index<&f32> for Test {
    type Output = i32;

    fn index(&self, _key: &f32) -> &Self::Output {
        &self.value
    }
}

impl MyTrait<&f32> for Test {}
edwardw
  • 12,652
  • 3
  • 40
  • 51
  • Thanks for clearing up the first part. Regarding the second: I would like to have the trait as a shorthand. Instead of passing `T where T : Index<&f32, Output=i32>` around functions, I would like to use `T where T: MyTrait`. Just image that the supertrait is a lot more complex... – hfhc2 Jan 18 '20 at 19:00
  • @hfhc2 in that case, passing `T: MyTrait<&f32>` around will do. As a side note, there's a unstable feature trait alias to do just that. See [this Q/A](https://stackoverflow.com/questions/59720444/replacing-a-trait-bound-with-a-trait-alias-says-the-size-for-values-cannot-be-k/59720554#59720554) for an example. But alas, it is still unstable. – edwardw Jan 18 '20 at 19:15
2

As far as I can tell (and I'm not 100% sure, the docs are rather silent on this), this:

impl Index<&f32> for Test { /* ... */ }

is shorthand for this:

impl <'a> Index<&'a f32> for Test { /* ... */ }

In other words, you implement the trait for any lifetime.

Analogously, you can require that the trait is implemented for any lifetime in your bound:

pub trait MyTrait:
    for<'a> Index<&'a f32, Output = i32> {
  // ...
}
trent
  • 25,033
  • 7
  • 51
  • 90
Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • I would only add, for people who want to know more about this feature, that the `for<'a>` syntax is called a *higher-ranked trait bound* (HRTB). See also [How does for<> syntax differ from a regular lifetime bound?](https://stackoverflow.com/q/35592750/3650362) – trent Jan 18 '20 at 19:07
  • Thank you very much. Could you recommend a source for these advanced lifetimes? – hfhc2 Jan 18 '20 at 19:12
  • @hfhc2 The [Rustonomicon](https://doc.rust-lang.org/nomicon/) – Sebastian Redl Jan 19 '20 at 16:40