【问题标题】:How to use mutable state in a loop?如何在循环中使用可变状态?
【发布时间】:2021-05-03 06:34:07
【问题描述】:

循环:

impl State {    
    pub fn exec(&mut self) -> Vec<(&[u8], SocketAddr)> {
        vec!()
    }
}

fn main_loop(mut state: State) {
    let mut packets = Vec::<(&[u8], SocketAddr)>::new();
    loop {
        let new = state.exec();
        packets.extend(new);
    }
}

给出错误:

error[E0499]: cannot borrow `state` as mutable more than once at a time
  --> src/main.rs:93:14
   |
93 |             let new = state.exec();
   |                       ^^^^^ `state` was mutably borrowed here in the previous iteration of the loop

但是,如果我注释掉 packets.extend(new); 行,则没有错误。

认为问题在于返回的向量归state 所有,就好像在循环迭代之前删除了new,这很好。如果可以修复返回的向量,我很高兴能够复制它。

我预计将.clone() 添加到newstate.exec() 会修复它,但它没有。

有没有简单的方法来解决这个问题?

或者我是否需要以不同的方式构建单线程应用程序状态?

【问题讨论】:

  • 您可以尝试返回对向量的引用吗?
  • @mousetail 我认为任何由可变状态方法返回的引用都会停止循环编译。我是否理解正确?
  • exec需要带&amp;mut self吗?你能不能有两个单独的方法,一个做突变,一个返回切片,后者采用&amp;self

标签: rust borrow-checker


【解决方案1】:
pub fn exec(&mut self) -> Vec<(&[u8], SocketAddr)>

让我们添加implicit lifetimes

pub fn exec<'a>(&'a mut self) -> Vec<(&'a [u8], SocketAddr)>

因此返回的切片必须与可变借用具有相同的生命周期。现在,您如何处理返回的切片?

let new = state.exec();
packets.extend(new);

我们将其存储在packets。所以state的借用必须至少和packets的类型一样长,这就是整个函数。因此,从循环的第一次迭代到函数的最后,我们永远不能再次借用state,因为可变借用一直持续到函数的末尾。

要问自己的第一个问题是:你真的需要可变借用吗?如果你能下到&amp;self,那么就这样做,你的问题就解决了。否则,您将需要以某种方式重组事物。 &amp;[u8] 类型本质上是无主的。它是对其他人数据的引用。如果您获得该数据的唯一方法是通过&amp;mut self,那么您将永远无法以您想要的方式循环。因此,要克隆数据,您需要使用to_vec,它将&amp;[u8] 转换为Vec&lt;u8&gt;(您的类型签名必须更改以反映这一点)。

【讨论】:

  • 谢谢,这个解释很清楚。通过实验,我发现&amp;[u8] 是问题所在。这解释了原因。
  • " 因为可变借用一直持续到函数结束" 在这种情况下没有可变借用同样如此,也许这句话具有误导性。在循环代码中使用非可变借用就可以了。
【解决方案2】:

只需更改函数签名以使返回的&amp;[u8] 不会从*self 借用即可编译示例代码:

    pub fn exec<'a>(&mut self) -> Vec<(&'a [u8], SocketAddr)> {
        vec![]
    }

注意'a 是一个免费的生命周期参数,所以调用者可以在那里提供任何东西(包括'static),并且exec 返回的任何引用都必须满足调用者提供的生命周期。在实践中,这意味着 Vec 只能包含 'static 引用,因为 'static 是唯一的生命周期,它是所有其他生命周期的子类型。

这不太可能是您想要的。但是,它可能是,或者[u8] 可能是从其他一些参数(不是*self)中借来的,或者是从*self 中包含的引用中重新借来的。这表明,简单地更改签名以更准确地表示输入和输出之间的生命周期关系有时可以在不进行任何其他更改的情况下修复此类错误。

【讨论】:

  • 谢谢,这真的增加了我的理解。默认生命周期并不总是我们需要的。
【解决方案3】:

一种解决方案是:

impl State {    
    pub fn exec(&mut self) -> Vec<(Vec<u8>, SocketAddr)> {
        vec!()
    }
}

fn main_loop(mut state: State) {
    let mut packets = Vec::<(Vec<u8>, SocketAddr)>::new();
    loop {
        let new = state.exec();
        packets.extend(new);
    }
}

我不确定这是不是最好的解决方案,所以我会暂时搁置这个问题。

【讨论】:

    猜你喜欢
    • 2013-08-11
    • 1970-01-01
    • 2022-07-26
    • 2022-08-18
    • 2021-05-06
    • 1970-01-01
    • 1970-01-01
    • 2018-06-09
    • 2013-12-03
    相关资源
    最近更新 更多