Pull to refresh

Comments 12

impl Rectangle {
    fn new() -> Rectangle {
        Rectangle { width: 0, height: 0 }
    }

    fn set_width(&mut self, width: u32) -> &mut Rectangle {
        self.width = width;
        self
    }
    
    fn build(self) -> Rectangle {
        self
    }
}

Жесть, на курсах такой же говнокод учите писать? Самое смешное, что вы даже не проверили код, он не рабочий:

let rect = Rectangle::new().set_width(10).set_height(20).build();
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- value moved due to this method call
   |                |
   |                move occurs because value has type `Rectangle`, which does not implement the `Copy` trait

Ну и до кучи, чтобы мутировать поля достаточно просто добавить mut, к чему эти пляски с недобилдером были?

let rect = Rectangle::new().set_width(10).set_height(20).build();
// rect.width = 15; // ошибка, так как rect неизменяемый после создания
let mut rect = Rectangle::new().set_width(10).set_height(20).build();
rect.width = 20; // ой, мутировали

Тут скорее хотели показать приватные поля в структуре. Но надо было структуру переместить в другой файл/модуль (ключевое слово mod):

// сама структура публичная, а поля - приватные
#[derive(Debug)]
pub struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    /// Других способов создать объект не существует
    pub fn new() -> Self {
        Self { width: 0, height: 0 }
    }
}

Если бы хотели - так бы и написали. Тут скорее хотели порекламировать свои курсы, но забыли, что раст они не знают. Как можно воспринимать это всерьёз, если они пишут вот такое:

Если создается экземпляр структуры с помощью let, все его поля будут неизменяемыми, если только каждое поле явно не объявлено как mut

Явно видно непонимание иммутабельности. К примеру,

let rect = Rectangle::new().set_width(10).set_height(20).build();
// rect.width = 15; // ошибка, так как rect неизменяемый после создания

Во‐первых, код не работает. Вы не можете скормить .build() &mut self вместо self, а именно первое возвращает .set_height.
Во‐вторых, build полностью бесполезна. Иммутабельность относится к конкретному месту размещения, если вы владеете объектом, то вы всегда можете сделать его мутабельным. Реальная иммутабельность достигается инкапсуляцией и удалением из общего доступа методов, которые могут изменить состояние объектов. Соответственно в шаблоне создатель у вас должен был быть тип RectangleBuilder и тип Rectangle, и только первый должен иметь методы set_width.
Соответственно, говорить «создадим иммутабельную структуру», не объясняя нигде, что она иммутабельная только пока в общем доступе нет методов, её изменяющих, некорректно. Напомню, что вот это вполне себе корректный код:

let mut rect = Rectangle::new();
rect.set_width(10);
let rect = rect.build();
let rect = rect;  // То же, что и строчка выше.
// rect.set_width(20);  // Не скомпилируется.
let mut rect = rect;
rect.set_width(30);  // А теперь мы опять можем менять rect.

Остальная часть не лучше. Например:

Если создается экземпляр структуры с помощью let, все его поля будут неизменяемыми, если только каждое поле явно не объявлено как mut:

Можете показать, как вы объявляете поля структуры как mut? Только так, чтобы результат компилировался.

Или «создадим глобально доступный Arc»: «код, в котором Arc нигде не упоминается».

Иммутабельность в Rust в нескольких предложениях:

  1. Если вы не объявили что‐то, как mut, то взятие исключающей (&mut) ссылки вам недоступно, как и всё, что требует её взятия.

  2. Изменение значения требует такой ссылки (или возможности её взятия) или внутренней мутабельности в каком‐то виде.

  3. Исключающая ссылка гарантирует только, что объект больше никому не доступен. Разделяемая ссылка (&) гарантирует только, что на объект нет исключающих ссылок. Иммутабельность здесь вообще не причём, ссылки исторически назвали неправильно, но большинство типов компилятор позволит менять только по исключающей ссылке.

// А теперь мы опять можем менять rect.

В смысле "можем менять rect"?? Вы же в курсе, что let mut rect = rect создаёт новый объект и мувает в него старый, а не снимает с объекта константность? Выведите на печать &rect до и после выражения с let, они будут разными.

Это с точки зрения семантики языка. С точки зрения программиста это часто один и тот же объект, т.к. нет видимой разницы (разные адреса, если оптимизатор почему‐то не смог убрать перемещение, не считаются). Это особенно верно, если под rect у нас не простая структура данных, а что‐то вроде Box<Rectangle>: кого волнует, где в стеке указатель на Rectangle, если данные всё равно за указателем и никуда не двигаются?

После некоторого размышления у меня появилось сильное подозрение, что семантика let foo = bar; — это «создать новое место foo и переместить в него объект из bar» (объект один и тот же, но его переместили), а не «создать новый объект foo и переместить в него данные из bar, bar удалить» (есть два разных объекта с непересекающимися временами жизни).
Я не могу найти конкретных подтверждений в документации (но то, как про перемещения объектов значений рассказывает документация std::pin оставляет ощущение, что я скорее прав), но let foo = foo точно не запускает Drop::drop и при этом никаких конструкторов в Rust нет. Можете обосновать утверждение «let mut rect = rect создаёт новый объект»?

Скорее создаёт новое имя, для существующего объекта. Чтобы создать место, имя нужно запинить.

это «создать новое место foo и переместить в него объект из bar» (объект один и тот же, но его переместили)

Именно так. Nomicon например явно говорит об этом: https://doc.rust-lang.org/nomicon/constructors.html. Мб я не так выразился, я имел в виду что объект всё же создаётся не полностью эквивалентный. У него другая позиция в памяти, +на старый объект после мува инвалидируются ссылки. "let mut rect = rect" это буквально то же самое, что "let mut rect2 = rect".

А зачем fn new() в таком виде если есть трейт Default?

let rect = Rectangle{width: 10, height: 20}; // короче и понятнее 
let rect = Rectangle{width: 20, ..rect}; // не мутировали, но в тему статье

Sign up to leave a comment.