Trait Generic Parameters, please help

Hi,

I have difficulties understanding an example given in this page Tour of Rust's Standard Library Traits | Generic Parameters.

I'm reproducing the code below, with some println!, and fn main() added:

// trait declaration generalized with lifetime & type parameters
trait Trait<'a, T> {
    // signature uses generic type
    fn func1(arg: T);
    
    // signature uses lifetime
    fn func2(arg: &'a i32);
    
    // signature uses generic type & lifetime
    fn func3(arg: &'a T);
}

struct SomeType;

// Mine.
impl SomeType {
    fn print_me(self) {
        println!("I am struct SomeType.fn print_me(self).");
    }
}

impl<'a> Trait<'a, i8> for SomeType {
    fn func1(arg: i8) { println!("func1 a i8 {}", arg); }
    fn func2(arg: &'a i32) { println!("func2 lifetime a i32 {}", arg); }
    fn func3(arg: &'a i8) { println!("func3 lifetime a i8 {}", arg); }
}

impl<'b> Trait<'b, u8> for SomeType {
    fn func1(arg: u8) { println!("func1 b u8 {}", arg); }
    fn func2(arg: &'b i32) { println!("func2 lifetime b i32 {}", arg); }
    fn func3(arg: &'b u8) { println!("func3 lifetime b u8 {}", arg); }
}

fn main() {
    <SomeType as Trait<i8>>::func1(34);
    <SomeType as Trait<i8>>::func2(&34);
    <SomeType as Trait<i8>>::func3(&34);

    <SomeType as Trait<u8>>::func1(34);
    <SomeType as Trait<u8>>::func2(&34);
    <SomeType as Trait<u8>>::func3(&34);    
}

Please help with the following questions:

Question

In fn main(), am I call the trait methods correctly, please?

Question

If I WAS calling them correctly, then what were the lifetime annotations 'a and 'b for, please?

I understand that The Book states:

We must only annotate types when multiple types are possible. In a similar way, we must annotate lifetimes when the lifetimes of references could be related in a few different ways. Rust requires us to annotate the relationships using generic lifetime parameters to ensure the actual references used at runtime will definitely be valid.

Is it in anyway related to the above quote, please?

Thank you and best regards,

...behai.

"Correctly" in what sense? The code you posted compiles, but you could readily verify that for yourself by pasting it into the Playground.

They denote the validity of the references being passed, just like in any other case. I don't understand what specifically you are looking for here.

1 Like

Yes, because your code compiles fine.

Absolutely related. And that quote is definitely the seriously underestimated prior knowledge to understand lifetime annotations.

In your case, 'a or 'b is a plain lifetime with no interaction with any other lifetimes, so it's trivially means a given lifetime from a borrow.

1 Like

Good morning vague,

Thank you for the explanation. I appreciate it.

Best regards,

...behai.

Good morning H2CO3,

Thank you for helping me...

  • "Correctly" in what sense? -- I did think about this when posting the question, I don't remember seeing the as keyword before, <SomeType as Trait> is from this Stackoverflow answer, I was expecting the compiler gives error about the lifetime and the generic type, but it only suggests adding <i8> (and <u8>). The code is not mine, it compiles and works as expected. That is the reason why I am uncertain and decided to ask for help.

  • I don't understand what specifically you are looking for here. -- it is related to the uncertainty I have above, I was expecting (wrongly) the compiler complains about lifetime, but it does not. Obviously, I still don't fully get lifetime yet... but I will.

Best regards,

...behai.

Google "lifetime elision".

1 Like

Thank you. I will. I have seen that in "The Book" -- but I will study it again.

Best regards,

...behai.

Hi,

On lifetime elision, for info, if new readers happen to find this thread in the future.

"The Book", Chapter 10 | Generic Types, Traits, and Lifetimes last section https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html, explains lifetimes, I did not get it the first time round, now that I went back and re-read, it helps enourmously.

-- Especially the three rules.

I think I understand lifetime elision a bit better now :slight_smile: And I can exercise the last example longest_with_an_announcement, too:

use std::fmt;

fn longest_with_an_announcement<'a, T>(
    x: &'a str,
    y: &'a str,
    ann: T,
) -> &'a str
where
    T: fmt::Display,
{
    println!("Announcement! {}", ann);
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

struct BeHai<'a> {
    text: &'a str,
}

impl<'a> fmt::Display for BeHai<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
      write!(f, "BeHai.text <{}>", &self.text)
    }
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let reader = String::from("John");

    let res = longest_with_an_announcement::<BeHai>(
        &reader.as_str(),
        &novel.as_str(),
        BeHai{text: String::from("asking too many questions").as_str()}
    );

    println!("res {}", res);
}

Best regards,

...behai.