【问题标题】:A borrow issue while trying to use std::util::replace尝试使用 std::util::replace 时出现借用问题
【发布时间】:2014-01-22 04:38:00
【问题描述】:
use std::util::replace;

假设我们有这些结构:

struct Foo {
    a: ~[Baz],
}

struct Bar {
    a: ~[Quux],
}

struct Baz {
    x: bool
}

struct Quux {
    x: bool,
    y: Bar,
}

以下工作正常:

    let mut foo = Foo{a: ~[Baz {x: true},
                           Baz {x: false}]};

    println!("{}, {}", foo.a[0].x, foo.a[1].x); // true false
    replace(&mut foo.a[0], foo.a[1]);
    println!("{}, {}", foo.a[0].x, foo.a[1].x); // false false

但是,这不起作用:

    let mut bar = Bar{a: ~[Quux {x: true, y: Bar {a: ~[]} },
                           Quux {x: false, y: Bar {a: ~[]} }]
    };

    println!("{}, {}", bar.a[0].x, bar.a[1].x); // true false

    replace(&mut bar.a[0], bar.a[1]);
}

编译器给出的错误是:

foobar_borrow2.rs:35:28: 35:36 error: cannot move out of `(*bar.a)[]` because it is borrowed
foobar_borrow2.rs:35     replace(&mut bar.a[0], bar.a[1]);
                                                ^~~~~~~~
foobar_borrow2.rs:35:13: 35:26 note: borrow of `(*bar.a)[]` occurs here
foobar_borrow2.rs:35     replace(&mut bar.a[0], bar.a[1]);
                                 ^~~~~~~~~~~~~

我不明白这是什么意思。这似乎与前一个案例相似,它有效。为什么将Bar 字段添加到Quux 会导致它失败?

【问题讨论】:

    标签: rust


    【解决方案1】:

    我想我现在明白了,但首先我认为值得简化。让我们把Quux改成:

    struct Quux {
            x: ~bool,
    }
    

    这会导致同样的错误。回想一下Foo.aBaz 的唯一向量。 Baz 具有复制语义,因为它是一个结构,其字段都具有复制语义(即单个 int 字段)。所以将foo.a[1] 传递给replace 的第二个参数会导致其值被复制。

    另一方面,Bar.aQuux 的唯一向量。 Quux 具有移动语义,因为它的唯一字段是拥有的指向布尔值的指针。因此,当我们将bar.a[1] 传递给replace 时,它会尝试将向量bar.a 的所有权转移到replace。但我们不能这样做,因为(显然?)在replace 的第一个参数中采用bar.a[0] 的可变借用导致整个向量已经被借用。

    我们可以在这个简单的例子中看到关于向量的最后一个事实:

    let mut x = ~[1, 5, 9];
    let y = &mut x[0];
    let z = &x[1]; //error: cannot borrow (*x)[] as immutable because it is already borrowed as mutable
    

    在我的原始示例中,Quux 具有移动语义,因为它具有 Bar 字段,Bar 具有移动语义,因为它具有向量作为其字段之一。所以同样的原则也在起作用。

    【讨论】:

      【解决方案2】:

      这里的替换函数取得了在编译器第一次使用bar时借用了bar,也就是bar.a[0]中的:

      replace(&mut bar.a[0], bar.a[1]);

      在那之后,直到函数完成,编译器会阻止进一步使用bar。不幸的是,这阻止了函数的第二个参数被接受,因为它目前被采用

      另一种方法是将bar.a[1] 的内容复制到一个新变量中,并将这个新变量作为第二个参数发送给replace()

      let a1 = bar.a[1].clone();
      replace(&mut bar.a[0], a1);
      

      这会将 bar.a[1] 中的值复制到一个新变量中。

      这需要 bar 及其内容来实现特征 std::clone::Clone。对于此示例,将 #[deriving(Clone] 添加到 Bar 和 Quuz 将起作用。

      #[deriving(Clone)]
      struct Quux {
          x: bool,
          y: Bar,
      }  
      
      #[deriving(Clone)]
      struct Bar {
          a: ~[Quux],
      }
      

      【讨论】:

      • 为什么replace函数在第一个参数后取得bar的所有权,而不是foo的所有权? foo 是不是也发生了同样的事情?
      猜你喜欢
      • 2019-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-18
      相关资源
      最近更新 更多