Understanding for<'d> in Rust

I've read https://doc.rust-lang.org/reference/trait-bounds.html#higher-ranked-trait-bounds 3 times but I couldn't understand what the where for<'a> means in Rust.

I've seen things like this in some code:

    impl<'b, 'c, 'e, DeviceT> InterfaceBuilder<'b, 'c, 'e, DeviceT>
            where DeviceT: for<'d> Device<'d>

and about this:

fn call_on_ref_zero<F>(f: F) where for<'a> F: Fn(&'a i32) {

what is Fn(&'a i32) ? What the parenthesis do?

I truly don't understand whats happening

The short explanation is that this

fn call_on_ref_zero<F>(f: F)
    for<'a> F: Fn(&'a i32)

is shorthand for this

fn call_on_ref_zero<F>(f: F)
    F: Fn(&'a i32),
    F: Fn(&'b i32),
    F: Fn(&'c i32),

Here the bound is repeated infinitely many times for every possible lifetime. It means that F is a closure that can be called with any &i32 no matter what lifetime it has.

how could the compiler implement this? Even though lifetimes are not generics I still imagine them as being something concretely implemented as code. I understood your answer as an analogy. Of course it's impossible to generate code for each lifetime in the program as it'd generate millions of lines.

Perhaps the lifetime checking is made in a dynamic way in the runtime?

Sorry if I'm talking nonsense, I'm still understanding lifetimes as well. I understand them as annotations for the compiler to check if something lives as far as it's needed.

Lifetimes are definitely both generics and part of the type in the sense that &'a u32 and &'b u32 are different types; it's just that unlike with type-based generics, lifetime-based generics are not monomorphized, which means that each choice of generic lifetime does not duplicate the function in the binary.

1 Like

so I can think of a lifetime bound as simply a rule, written in the type, that says,
for a T: &mut 'a Something, "accept types with lifetime greater or equal 'a"?

Then whenever I pass a variable that binds to T, compiler verifies its lifetime and passes or throws an error.

In that case, I wonder how would the where DeviceT: for<'d> Device<'d> be translated into code. For each DeviceT<'a, 'b, 'c, '...> passed, it'd generate a code. In each of these codes, the accepted type for T would have to have lifetime <'a, 'b, 'c, '...>?

This is not valid syntax: T: &mut 'a Something. Perhaps you meant T: 'a ?

As for the DeviceT thing, these are the same:

// this
fn call_on_ref_zero<F>(f: F)
    for<'a> F: Fn(&'a i32),
// is the same as this
fn call_on_ref_zero<F>(f: F)
    F: for<'a> Fn(&'a i32),

It's just that the former expands into many bounds, whereas the latter expands into

fn call_on_ref_zero<F>(f: F)
    F: Fn(&'a i32) + Fn(&'b i32) + Fn(&'c i32) + ...,

Writing it with plusses or multiple bounds has the same effect. Regarding your device, it would be

where DeviceT: Device<'a> + Device<'b> + Device<'c> + ...

and not

where DeviceT: Device<'a, 'b, 'c, ...>

Because Device only takes a single lifetime as argument — it doesn't make sense to give it more than one, and certainly not infinitely many.

It is saying there is to be some implementation;

impl<'d> Device<'d> for MyStruct { ... }

Note: these are identical

where DeviceT: for<'d> Device<'d>
where for<'d> DeviceT: Device<'d>

You have Traits and Generic Traits. Generic Traits allow the compiler to make Traits without the developer repeating lots of code. Rust uses type inference so often you only write the short name and nothing more. If forced to you add ::<X,Y,Z>

Fn is a Generic Trait. The language requires the alternate parenthesis syntax to know the real Trait.

Lifetimes are put into a constraints solver (similar, but simpler than SAT solvers like z3), and that checks for satisfiability. There ars no runtime checks for lifetimes, in fact the Rust compiler promptly forgets about lifetimes after checking their correctness and treats references like raw pointees internally.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.