【问题标题】:Why can't Rust downgrade my lifetime but instead complains about type mismatch?为什么 Rust 不能降级我的生命周期而是抱怨类型不匹配?
【发布时间】:2022-06-29 02:36:27
【问题描述】:

考虑以下结构:

struct State<'a> {
    parent: Option<&'a mut State<'a>>,
    // ...
}

我的状态存储了一些我以后可能需要的值。现在我想实现子状态,即允许在不触及父状态的情况下操作子状态中的这些值,但将不在子状态中的值的查找转发到其父状态。不幸的是,我始终需要对每个父状态的可变引用。我尝试了以下方法,但它不起作用(Playground):

impl<'a> State<'a> {
    fn substate<'b>(&'b mut self) -> State<'b>
    where
        'a: 'b,
    {
        State::<'b> { parent: Some(self) }
    }
}

这会给出以下错误消息:

error[E0308]: mismatched types
  --> src/main.rs:10:36
   |
10 |         State::<'b> { parent: Some(self) }
   |                                    ^^^^ lifetime mismatch
   |
   = note: expected mutable reference `&mut State<'b>`
              found mutable reference `&mut State<'a>`
note: the lifetime `'b` as defined here...
  --> src/main.rs:6:17
   |
6  |     fn substate<'b>(&'b mut self) -> State<'b>
   |                 ^^
note: ...does not necessarily outlive the lifetime `'a` as defined here
  --> src/main.rs:5:6
   |
5  | impl<'a> State<'a> {
   |      ^^

我不明白为什么编译器希望'b'a 寿命更长。事实上,一个状态的父状态总是比它的子状态寿命长,所以在我的情况下,情况总是相反。那么为什么编译器不能将“更长”生命周期'a 降级为“更短”生命周期'b

【问题讨论】:

标签: rust types lifetime


【解决方案1】:

nightly 上的错误信息更好(感谢NLL stabilization 在稳定之前可能会被删除,但现在它在那里):

error: lifetime may not live long enough
  --> src/main.rs:10:31
   |
5  | impl<'a> State<'a> {
   |      -- lifetime `'a` defined here
6  |     fn substate<'b>(&'b mut self) -> State<'b>
   |                 -- lifetime `'b` defined here
...
10 |         State::<'b> { parent: Some(self) }
   |                               ^^^^^^^^^^ this usage requires that `'b` must outlive `'a`
   |
   = help: consider adding the following bound: `'b: 'a`
   = note: requirement occurs because of a mutable reference to `State<'_>`
   = note: mutable references are invariant over their type parameter
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

'a 的寿命超过'b 是不够的,它应该完全相同,因为它是不变的,并且不变的生命周期不能缩短。查看编译器提供的链接或the reference entry about variance 以更好地了解什么是方差以及为什么可变引用是不变的。

【讨论】:

  • 同意,那个错误信息确实好多了。有什么方法可以在这里进行子类型化吗?据我所知,在我的情况下可以降级生命周期,只是编译器不知道。
  • @msrd0 无法选择退出差异。但是,使用可变引用两次使用相同的生命周期几乎总是错误的。如果我不得不猜测,我猜您正在尝试创建一个自引用结构并由于编译器错误而到达此注释。我说的对吗?
  • 不,没有任何东西是自我参照的。我只想创建子状态的子状态的子状态...除了我需要跟踪何时访问部分状态,因此是可变引用
  • @msrd0 如果没有更多代码(这可能适用于新问题),我无法为您提供帮助,但我并不认为在这里忽略差异是合理的。我认为这里仍然存在“喵喵叫”问题。
  • 我最终使用了RefCell,与可变引用不同,它似乎并不能防止对生命周期参数进行子类型化。
【解决方案2】:

函数签名不正确,下面是一个示例说明原因:

struct State<'a> {
    parent: Option<&'a mut State<'a>>,
}

impl<'a> State<'a> {
    fn substate<'b>(&'b mut self) -> State<'b>
    where
        'a: 'b,
    {
        /* implementation hidden but assumed to be implemented as described */
    }
}
// [root]
let mut root = State { parent: None };

// [foo] -> [root]
let mut foo = root.substate();

{
    // [bar] -> [foo] -> [root]
    let mut bar = foo.substate();

    // [bar] -> [foo] -> [tmp]
    let mut tmp = State { parent: None };
    bar.parent.as_mut().unwrap().parent = Some(&mut tmp);

    // tmp is dropped
}

// [foo] -> {uninitialized memory}
drop(foo);

(Full playground example)

函数签名允许我们在foo 中存储一个比foo 本身寿命短的引用,所以在我们离开块后foo 指向未初始化的内存,这是未定义的行为并且不好

如果你用cargo miri运行上面的代码,会报这个错误,确认确实是未定义的行为:

error: Undefined Behavior: type validation failed at .parent.<enum-variant(Some)>.0: encountered a dangling reference (use-after-free)
  --> src/main.rs:40:10
   |
40 |     drop(foo);
   |          ^^^ type validation failed at .parent.<enum-variant(Some)>.0: encountered a dangling reference (use-after-free)
   |
   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

   = note: inside `main` at src/main.rs:40:10

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to previous error

【讨论】:

    猜你喜欢
    • 2021-06-27
    • 1970-01-01
    • 1970-01-01
    • 2021-04-02
    • 2020-11-12
    • 2021-12-22
    • 1970-01-01
    • 2019-04-08
    • 1970-01-01
    相关资源
    最近更新 更多