为什么Rust“From”trait实现给予生存期错误,而自定义trait不会?

x8diyxa7  于 2023-01-21  发布在  其他
关注(0)|答案(2)|浏览(122)

当我使用自定义trait时,下面的代码可以工作,但是我不明白为什么当尝试使用内置的"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: 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`
xfb7svmp

xfb7svmp1#

由于您没有为From<&SimpleEnum> impl提供任何生存期,编译器会为&SimpleEnum(以及参数value)分配一个与Self(或&A)的生存期 * 不同 * 的生存期。

impl<'a, 'b> From<&'a SimpleEnum> for &'b A {
    fn from(value: &'a SimpleEnum) -> Self {
        if let SimpleEnum::A(value) = value {
            value
        } else {
            panic!();
        }
    }
}

这是行不通的,因为Self的生存期'bvalue的生存期'a不同,你必须明确告诉编译器这两个生存期是相同的:

impl<'a> From<&'a SimpleEnum> for &'a A {
    fn from(value: &'a SimpleEnum) -> Self {
        if let SimpleEnum::A(value) = value {
            value
        } else {
            panic!();
        }
    }
}

Playground

o4tp2gmn

o4tp2gmn2#

这种差异与推断的寿命有关。
您的自定义trait在参数和返回类型中都有引用;由于lifetime elision rules,您的trait等效于以下内容:

trait FromCustom {
    fn from_custom<'a>(value: &'a SimpleEnum) -> &'a A;
}

因此,仅通过签名,返回类型就绑定到参数的生存期。
相比之下,From特征是这样写的:

pub trait From<T>: Sized {
    fn from(value: T) -> Self;
}

TSelf之间没有生命期的隐式链接,因为没有明显的生命期在使用中,因此当你编写实现时,&SimpleEnum&A有 * 不同的 * 生命期,就像这样写:

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!();
        }
    }
}

由此推断,any lifetime的&SimpleEnum可以用来创建 any lifetime的&A,这是不正确的。相反,您需要传达具有特定lifetime的&SimpleEnum可以用来创建具有该lifetime的&A。您可以自己将这些lifetime链接在一起:

impl<'a> From<&'a SimpleEnum> for &'a A {
    fn from(value: &'a SimpleEnum) -> Self {
        if let SimpleEnum::A(value) = value {
            value
        } else {
            panic!();
        }
    }
}

相关问题