Reputation: 27564
Rust supports trait inheritance, as follows:
pub trait A {}
pub trait B: A {}
B: A
means that if some type T
implements B
, it also needs to implement all the methods in A
.
But today I see the following code:
trait Display: 'static {
fn print(&self);
}
What does it mean? It doesn't seem to be trait inheritance.
Upvotes: 7
Views: 1179
Reputation: 3714
While other answers have already mentioned that this is a Lifetime Bound, that term can be a bit confusing so let me try to add a different explanation in the hopes that it might help some people.
Lifetime Bounds are effectively a special kind of (auto) trait.
The 'a
'trait' / lifetime bound is satisfied by all types that contain no references that might live shorter than 'a.
That's all there is to it.
'static
is then just a special case that is satisfied by all types that contain no references that will ever become invalid.
Here's an example of why this concept is useful/necessary:
// Basic definitions for demo purposes
trait Foo {
fn get_value(&self) -> &i32;
}
struct Bar<'a>{
value: &'a i32
}
impl<'a> Foo for Bar<'a>{
fn get_value(&self) -> &i32 {
self.value
}
}
// Now, this seemingly innocent method already cannot be allowed by the compiler.
// As for why, see below in `main`
fn push_foo(tgt: &mut Vec<Box<dyn Foo>>, foo: impl Foo) {
tgt.push(Box::new(foo));
}
// demonstration why `push_foo` must be illegal:
fn main() {
let mut list_of_foos = Vec::new();
{
let value = 42;
let bar = Bar { value: &value };
push_foo(&mut list_of_foos, bar);
}
// `value` went out of scope at this point, so this would be a use after free bug
// therefore, the `push_foo` function cannot be allowed to compile
let _access_value_after_scope_ended = *list_of_foos[0].get_value();
}
// The simple fix for this issue is to force `foo` to not contain any
// data that has *any* additional lifetime bounds (== `foo` shall satisfy the 'static Lifetime Bound).
// (The `Box<dyn Foo>` in this signature is a shorthand for `Box<dyn Foo + 'static>`)
fn push_foo_fixed_v1(tgt: &mut Vec<Box<dyn Foo>>, foo: impl Foo + 'static) {
tgt.push(Box::new(foo));
}
// To fix the problem more generally, we could express the following additional guarantee:
// There exists a lifetime `'a` that is at least as long as any Lifetime Bound on
// `foo` or the `dyn Foo`s in `tgt`:
fn push_foo_fixed_v2<'a>(tgt: &mut Vec<Box<dyn Foo + 'a>>, foo: impl Foo + 'a) {
tgt.push(Box::new(foo));
}
[ Rust Playground ]
Upvotes: 0
Reputation: 42227
Rust supports trait inheritance, as follows [...] B: A means that if some type T implements B, it also needs to implement all the methods in A.
Technically, that is not inheritance but requirement. It is a trait bound not entirely dissimilar to one you'd have in a function: it constraints the type on which B is implementable to only types on which A is already implemented.
With that change in wording, the second version is much easier to understand: it's a lifetime bound, meaning it constraints the type on which B is implementable to only types with 'static
lifetime, meaning if you're trying to implement B on a type, that must either have no lifetime at all, or have a 'static
lifetime (or the implementation must have a lifetime bound aka only work for some uses of the type).
You can see that if you try to implement the trait on a lifetime-generic structure:
struct A<'a>(&'a str);
trait Display: 'static {
fn print(&self);
}
impl <'a>Display for A<'a> {
fn print(&self) { todo!() }
}
will yield
error[E0478]: lifetime bound not satisfied
That is because 'a
can be anything, so implementing Display
for A<'a>
means it is also implemented for non-'static
instances, which is not valid.
By adding the relevant lifetime bound on the impl, and thus limiting the implementation to A<'static>
instances:
struct A<'a>(&'a str);
trait Display: 'static {
fn print(&self);
}
impl <'a: 'static>Display for A<'a> {
fn print(&self) { todo!() }
}
the requirements of the trait are satisfied, and the impl is valid (nb: the 'a
is not necessary here you can just impl ... for A<'static>
, I'm showing it for regularity).
And if your struct has no lifetime it works by default, because no lifetime ~ 'static
:
struct A(String);
trait Display: 'static {
fn print(&self);
}
impl Display for A {
fn print(&self) { todo!() }
}
Upvotes: 6
Reputation: 382122
Rust doesn't have inheritance.
What it has is a way to define constraints. For example a trait may be constrained to only be implemented by types which implement another trait.
In your case the constraint is a lifetime bound.
To implement your Display
trait, an object may contain references but in this case their lifetime must respect this constraint.
Let's suppose you have this type:
struct S<'a> {
s: &'a str,
}
Then you can't implement the trait for any lifetime, but only 'static
.
impl Display for S<'static> {
fn print(&self){}
}
fn main() {
let s1 = "test";
let a = S { s: s1 };
a.print(); // compiles
let s2 = "test".to_string();
let a = S { s: &s2 };
a.print(); // doesn't compile because s doesn't live long enough
}
Upvotes: 5