Reputation: 2424
The following code can work when I use a custom trait. But I don't understand why it fails when trying to use the builtin "From" trait.
struct A {
x: isize,
}
enum SimpleEnum {
A(A),
B(u8),
}
trait FromCustom {
fn from_custom(value: &SimpleEnum) -> &A;
}
impl FromCustom for &SimpleEnum {
fn from_custom(value: &SimpleEnum) -> &A {
if let SimpleEnum::A(value) = value {
value // NO error
} else {
panic!();
}
}
}
impl From<&SimpleEnum> for &A {
fn from(value: &SimpleEnum) -> Self {
if let SimpleEnum::A(value) = value {
value // error
} else {
panic!();
}
}
}
Error:
error: lifetime may not live long enough
--> rust_pst_bin/src/main.rs:55:13
|
53 | fn from(value: &SimpleEnum) -> Self {
| - ---- return type is &'2 A
| |
| let's call the lifetime of this reference `'1`
54 | if let SimpleEnum::A(value) = value {
55 | value // error
| ^^^^^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
Upvotes: 1
Views: 142
Reputation: 7579
Since you do not provide any lifetimes to your From<&SimpleEnum>
impl, the compiler assigns to &SimpleEnum
(and hence the parameter value
) a different lifetime than the lifetime of Self
(or &A
). Desugared, the impl
would be as follows:
impl<'a, 'b> From<&'a SimpleEnum> for &'b A {
fn from(value: &'a SimpleEnum) -> Self {
if let SimpleEnum::A(value) = value {
value
} else {
panic!();
}
}
}
This would not work as Self
has a different lifetime 'b
compared to value
's lifetime 'a
. You have to explicitly tell the compiler that the two lifetimes are the same:
impl<'a> From<&'a SimpleEnum> for &'a A {
fn from(value: &'a SimpleEnum) -> Self {
if let SimpleEnum::A(value) = value {
value
} else {
panic!();
}
}
}
Upvotes: 3
Reputation: 60522
The difference has to do with inferred lifetimes.
Your custom trait has references in both the parameter and return type; due to lifetime elision rules, your trait is equivalent to the following:
trait FromCustom {
fn from_custom<'a>(value: &'a SimpleEnum) -> &'a A;
}
So by the signature alone, the return type is bound to the lifetime of the parameter.
In contrast, the From
trait is written as such:
pub trait From<T>: Sized {
fn from(value: T) -> Self;
}
There is no implicit linking of lifetimes between T
and Self
since there are no obvious lifetimes in use. And thus when you write your implementation, the &SimpleEnum
and &A
have distinct lifetimes, as if written like so:
impl<'a, 'b> From<&'b SimpleEnum> for &'a A {
fn from(value: &'b SimpleEnum) -> Self {
if let SimpleEnum::A(value) = value {
value // error
} else {
panic!();
}
}
}
What is inferred is that a &SimpleEnum
of any lifetime can be used to create a &A
with any lifetime, which is untrue. You instead need to communicate that a &SimpleEnum
with a particular lifetime can be used to create a &A
with that lifetime. You can do that by linking the lifetimes together yourself:
impl<'a> From<&'a SimpleEnum> for &'a A {
fn from(value: &'a SimpleEnum) -> Self {
if let SimpleEnum::A(value) = value {
value
} else {
panic!();
}
}
}
Upvotes: 2