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.
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, '...>?
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.