首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不能一次多次将`x`作为可变变量借用

不能一次多次将`x`作为可变变量借用
EN

Stack Overflow用户
提问于 2015-07-08 07:32:54
回答 2查看 14.9K关注 0票数 35

在以下代码(playground)中:

代码语言:javascript
复制
struct Node {
    datum: &'static str,
    edges: Vec<Node>,
}

fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node {
    node.edges.push(Node {
        datum: data,
        edges: Vec::new(),
    });
    &node.edges[node.edges.len() - 1] // return just added one
}

fn traverse<F>(root: &Node, callback: &F)
where
    F: Fn(&'static str),
{
    callback(root.datum);
    for node in &root.edges {
        traverse(node, callback);
    }
}

fn main() {
    let mut tree = Node {
        datum: "start",
        edges: Vec::new(),
    };

    let lvl1 = add(&mut tree, "level1");

    traverse(&mut tree, &|x| println!("{:}", x)); //I actually don't need mutability here
}

我有这个错误:

代码语言:javascript
复制
error[E0499]: cannot borrow `tree` as mutable more than once at a time
  --> src/main.rs:32:19
   |
30 |     let lvl1 = add(&mut tree, "level1");
   |                         ---- first mutable borrow occurs here
31 | 
32 |     traverse(&mut tree, &|x| println!("{:}", x)); //I actually don't need mutability here
   |                   ^^^^ second mutable borrow occurs here
33 | }
   | - first borrow ends here

我的问题似乎与Why does Rust want to borrow a variable as mutable more than once at a time?非常相似,但我不确定。如果是这样,有没有解决这个问题的办法?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-07-08 13:20:51

这是由于add的定义方式造成的:

代码语言:javascript
复制
fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node

这里指定结果引用的生存期应等于传入引用的生存期。唯一可能的方法(不安全代码除外)是结果引用以某种方式派生自传入引用,例如,它引用了传入引用所指向的对象内的某个字段:

代码语言:javascript
复制
struct X {
    a: u32,
    b: u32,
}

fn borrow_a<'a>(x: &'a mut X) -> &'a mut u32 {
    &mut x.a
}

但是,编译器无法通过只查看函数签名来确切知道传入结构中借用了什么(通常,这是编译使用此函数的代码时唯一能做的事情)。因此,它不能知道以下代码在技术上是正确的:

代码语言:javascript
复制
let mut x = X { a: 1, b: 2 };
let a = borrow_a(&mut x);
let b = &mut x.b;

我们知道ab是不相交的,因为它们指向结构的不同部分,但是编译器不能知道这一点,因为在borrow_a的签名中没有任何东西会暗示它(不可能有,Rust不支持它)。

因此,编译器能做的唯一明智的事情就是考虑借用整个x,直到删除borrow_a()返回的引用。否则,可能会为同一数据创建两个可变引用,这违反了Rust别名保证。

请注意,以下代码是正确的:

代码语言:javascript
复制
let mut x = X { a: 1, b: 2 };
let a = &mut x.a;
let b = &mut x.b;

在这里,编译器可以看到ab从不指向相同的数据,即使它们确实指向相同的结构内部。

这个问题没有变通的办法,唯一的解决方案是重新构造代码,这样它就不会有这样的借用模式。

票数 29
EN

Stack Overflow用户

发布于 2015-07-08 11:02:26

这种行为是合乎逻辑的。考虑一下什么

代码语言:javascript
复制
fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node

意思是。

这表示&mut Node的生命周期等于其返回值的生命周期。因为您将返回值分配给一个名称,所以它会一直存在到作用域的末尾。因此,可变的借入也存在这么长的时间。

如果您可以很容易地丢弃返回值,请这样做。你可以把它扔到地板上:

代码语言:javascript
复制
let mut tree = Node {
    datum: "start",
    edges: Vec::new(),
};

add(&mut tree, "level1");

traverse(&mut tree, &|x| println!("{:}", x));

或者,您可以使用词法作用域来约束它,而不必完全删除它。

如果你想借入返回值,而又不想让可变的借入保持那么长的时间,那么你可能不得不将函数一分为二。这是因为您无法从可变的borrow中借用返回值来执行此操作。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31281155

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档