Как я могу использовать объект как параметр его собственного метода в Rust?

Я написал следующий код для простой структуры в Rust. Это всего лишь пример, у него нет реальной логики:

struct Vec2 {
    x: f32,
    y: f32,
}

impl Vec2 {
    fn multiply(&mut self, other: &Vec2) {
        self.x *= other.x;
        self.y *= other.y;
    }
}

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

fn main() {
    let mut vec = Vec2 { x: 2.0, y: 2.3 };
    vec.multiply(&vec);
}
error[E0502]: cannot borrow `vec` as mutable because it is also borrowed as immutable
  --> src/main.rs:15:5
   |
15 |     vec.multiply(&vec);
   |     ^^^^--------^----^
   |     |   |        |
   |     |   |        immutable borrow occurs here
   |     |   immutable borrow later used by call
   |     mutable borrow occurs here

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

1 ответ

Решение

Я думаю, что вы уже понимаете это, но причина того, что у вас сейчас не работает, состоит в том, что стоимость не может быть заимствована в одно и то же время, что и неизменный заем. Чтобы выполнить метод как есть, вам потребуется как изменяемый заем (в форме &mut self) и неизменный заем (в форме other: &Vec2) в то же время. Поскольку ваш метод должен работать для любых двух членов Vec2у вас всегда будет два отдельных заимствования: компилятор не может сделать вывод, что один заем будет работать в случае умножения вектора на себя.

Что касается вашего вопроса, у вас есть несколько вариантов, в зависимости от деталей того, что вы пытаетесь сделать.

Вариант 1: клонирование

Просто добавь #[derive(Clone)] к вашему определению Vec2 и вы сможете клонировать элемент этого типа, используя clone метод.

#[derive(Clone)]
struct Vec2 {
    x: f32,
    y: f32,
}

impl Vec2 {
    fn multiply(&mut self, other: &Vec2) {
        self.x *= other.x;
        self.y *= other.y;
    }
}

fn main() {
    let mut vec = Vec2 { x: 2.0, y: 2.3 };
    vec.multiply(&vec.clone());
}

Вариант 2: копирование

Если ваш тип такой простой (всего два числа с плавающей запятой), то вы также можете получить Copy, Затем элементы типа просто копируются при подаче в функции, а не в ссылке. Это означает, что мы должны изменить подпись Vec2::multiply принять other так же просто Vec2, скорее, чем &Vec2 (это не является строго необходимым, но использование указателя обычно менее эффективно для Copy типов).

#[derive(Copy, Clone)]
struct Vec2 {
    x: f32,
    y: f32,
}

impl Vec2 {
    fn multiply(&mut self, other: Vec2) {
        self.x *= other.x;
        self.y *= other.y;
    }
}

fn main() {
    let mut vec = Vec2 { x: 2.0, y: 2.3 };
    vec.multiply(vec);
}

Вариант 3: выделенный метод

Вы могли бы иметь отдельный метод с именем multiply_self или же square (в зависимости от вашей семантики), который просто занимает &mut self, Это может быть вашим лучшим выбором в общем случае изменения структуры с использованием самой себя.

struct Vec2 {
    x: f32,
    y: f32,
}

impl Vec2 {
    fn multiply_self(&mut self) {
        self.x *= self.x;
        self.y *= self.y;
    }
}

fn main() {
    let mut vec = Vec2 { x: 2.0, y: 2.3 };
    vec.multiply_self();
}

Вариант 4: вернуть новое значение

Вы могли бы иметь метод, который не берет self изменчиво и вместо этого возвращает новый Vec2, Тогда вы могли бы сделать vec = vec.multiply(&vec),

struct Vec2 {
    x: f32,
    y: f32,
}

impl Vec2 {
    fn multiply(&self, other: &Vec2) -> Vec2 {
        Vec2 {
            x: self.x * other.x,
            y: self.y * other.y,
        }
    }
}

fn main() {
    let mut vec = Vec2 { x: 2.0, y: 2.3 };
    vec = vec.multiply(&vec)
}

Вероятно, есть много других способов сделать это, но это то, что приходит на ум в этом простом случае. Если вы поделитесь более подробной информацией о том, что вы пытаетесь сделать в целом, я мог бы предложить больше.

Другие вопросы по тегам