Изменяемость полей для структур в Rust

Я все еще новичок в Rust, но у меня есть сомнения относительно того, как изменчивость работает для полей в структурах. В частности, как мы можем изменить поля, которые изначально были неизменяемыми. Например:

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

impl Point {
    fn new(x: isize, y: isize) -> Self {
        Self { x, y }
    }
    fn set_x(&mut self, new_x: isize) {
        self.x = new_x;
    }
}

struct Line {
    p: Point,
    q: Point,
}

impl Line {
    fn new(p: Point, q: Point) -> Self {
        Self { p, q }
    }
    fn set_x_in_p(&mut self, new_x: isize) {
        self.p.set_x(new_x);
    }
}

fn main() {
    // Both x and y are immutable
    let p = Point::new(0, 0);
    let q = Point::new(1, 1);

    // Line IS mutable
    let mut line = Line::new(p, q);

   // Modifying point p (originally immutable) with a new x 
    line.set_x_in_p(999);
}

Вместо этого со ссылками мы не можем

   let x = 3;
   let y = &mut x; // does not work because x originally is immutable

Итак, как это работает? Спасибо.


person alqacer    schedule 09.04.2020    source источник


Ответы (2)


В вашем примере p и q действительно неизменяемы, но затем вы перемещаете их в экземпляр Line с помощью конструктора, поскольку они передаются по значению и не реализуют Copy для включения неявного копирования. Это означает, что исходные привязки (p и q) больше недействительны (компилятор не позволит вам их использовать), а вместо этого значения доступны только через изменяемую привязку line, которая позволяет изменять ее элементы. По сути, изменяемыми являются не значения, а их привязки. Например, следующий код можно использовать для повторной привязки значения, чтобы изменить его изменчивость:

let x = String::from("hello world"); // using String as an example of a non-Copy type

let mut x = x; // this shadows the previous binding with a mutable one of the same name
x.make_ascii_uppercase(); // we can now mutate the string
let x = x; // shadow the mutable binding with an immutable one

println!("Result: {}", x);

Этот пример работает, потому что мы имеем прямой контроль над значением и можем перемещать/привязывать его по желанию. Введение ссылок ограничило бы возможности — например, следующие примеры не будут работать:

let x = String::from("hello world");
let x_ref = &x; // create an immutable reference to x
let mut x_mut = x; // error - we can't move x while it's borrowed
let x_mut_ref = &mut x; // error - we can't create a mutable reference while any other references exist

Я рекомендую прочитать страницу владения и перемещений. Rust на примере, который объясняет это довольно хорошо.

person apetranzilla    schedule 10.04.2020

Когда вы объявляете x, вы указываете его как неизменяемый с помощью let вместо let mut. Когда вы затем объявляете y и инициализируете его как &mut x, вы пытаетесь заимствовать x. В Rust вы никогда не сможете одновременно иметь совместное владение и изменчивость.

Ознакомьтесь с что говорит Нико Мацакис о правах собственности.

person nairware    schedule 10.04.2020