如何从方法中改变结构的字段?


73

我想做这个:

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn up(&self) {
        self.y += 1;
    }
}

fn main() {
    let p = Point { x: 0, y: 0 };
    p.up();
}

但是这段代码会引发编译器错误:

error[E0594]: cannot assign to field `self.y` of immutable binding
 --> src/main.rs:8:9
  |
7 |     fn up(&self) {
  |           ----- use `&mut self` here to make mutable
8 |         self.y += 1;
  |         ^^^^^^^^^^^ cannot mutably borrow field of immutable binding

Answers:


108

您需要使用&mut self代替&self并使p变量可变:

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn up(&mut self) {
        // ^^^ Here
        self.y += 1;
    }
}

fn main() {
    let mut p = Point { x: 0, y: 0 };
    //  ^^^ And here
    p.up();
}

在Rust中,可变性是继承的:数据的所有者决定该值是否可变。但是,引用并不意味着所有权,因此引用本身可以是不可变的或可变的。您应该阅读解释所有这些基本概念的正式书


35
@VladimirMatveev我只想说,即使您以前从未看过这些概念,也没有读过这些书,但直到您遇到与之相关的实际情况时,它才有意义。就我而言,这些答案仍然非常有帮助;)
Aeolun

6
@Aeolun-说得很好。我浏览了《书》,以为我理解了这个概念,但是发现直到开始从事实际的Rust项目时,我才真正意识到这一点。
Syndog

3

通过使用Cell<T>您可以模拟字段级别的可变性:

use std::cell::Cell;

struct Point {
    x: i32,
    y: Cell<i32>,
}

impl Point {
    fn up(&self) {
        self.y.set(self.y.get() + 1);
    }
}

fn main() {
    let p = Point { x: 0, y: Cell::new(0) };
    p.up();
    println!("y: {:?}", p.y);
}

这将打印,y: Cell { value: 7 }并且我们已成功更新y

此外,如果使用的是nightly通道,则可以#![feature(cell_update)].rs文件顶部声明并在up()方法内部使用以下语法:

impl Point {
    fn up(&self) {
        self.y.update(|x| x + 1);
    }
}

注意:上面的此功能是仅夜间使用的实验API。

来自Rust 1.7的Rust编程语言


Box,Rc,Arc是否同样适用?
Michael Allwright

是的,它确实。你可以将它们组合起来,例如:Rc<Cell<i32>>... y: Rc::new(Cell::new(0)),
silvioprog
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.