2016-02-26 5 views
4

Я начал изучать Rust, и я пытаюсь реализовать простые одномерные клеточные автоматы. Я хочу представить состояние автоматов (Board) как структуру с размером и двумя разными векторами (одного размера). Я пробовал:Как поменять два поля структуры

struct Board { 
    n: usize, 
    cur: Vec<u32>, 
    next: Vec<u32>, 
} 

impl Board { 
    fn new(size: usize) -> Board { 
     Board { 
      n: size, 
      cur: vec![0;size], 
      next: vec![0;size], 
     } 
    } 
} 

Пока все хорошо. Я также могу мутировать оба вектора. Но я хочу, чтобы иметь возможность поменять оба вектора (или, вернее, их ссылки), такие как:

fn swap(&mut self) -> &Board { 
    let tmp = self.cur; 
    self.cur = self.next; 
    self.next = tmp; 
    self 
} 

Он выходит из строя, с cannot move out of borrowed content [E0507], который я думаю, что я могу понять. Я также попробовал mem::swap, который я нашел в подобном названии вопроса без успеха.

Как я могу заставить этот пример работать? (Так как я новичок в Rust, не стесняйтесь предлагать другое представление данных).

ответ

4

Как вы заметили, mem::swap это путь:

fn swap(&mut self) -> &Board { 
    std::mem::swap(&mut self.cur, &mut self.next); 
    self 
} 

Это работает. Обратите внимание, что при использовании . вы разыгрываете self. Так, в то время как self имеет тип &mut Board, self.cur имеет тип Vec<u32>. Поэтому компилятор жаловался на «выход из заемного контента», и нам нужны дополнительные &mut.

4

В чем проблема?

Вы отверстия перфорации в ваших данных:

fn swap(&mut self) -> &Board { 
    let tmp = self.cur;   // 1 
    self.cur = self.next;  // 2 
    self.next = tmp;   // 3 
    self 
} 

Если проанализировать построчно:

  1. self.cur теперь неинициализированным
  2. self.next теперь неинициализированным
  3. все кошерное получить

Если по какой-то причине вычисление прерывается до линии (3) имеет изменение ужесточении ситуацию, self теперь отравленный и может вызвать все виды неприятных вещей, чтобы это произошло. Примечательно, что его деструктор может попытаться освободить память дважды, например.

В теории, вы могли бы проверить компилятор для временных отверстий и доказать, без сомнения, что:

  • функция не обращается self в то время как отверстие пробито
  • в конце области, будь то это достигается нормально или путем разматывания, отверстия заполняются снова

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

А?

Ответ лежит на std::mem, который предоставляет функции для выполнения таких операций низкого уровня безопасным образом. Хотя сами функции реализованы с использованием unsafe под капотом, они опираются на свое понимание языка и среды выполнения, чтобы выявить безопасные интерфейсы.

двух частных функций, которые будут вам интересны следующие:

  • replace: заменяет содержание dest: &mut T по src: T и возвращает то, что было ранее содержавшиеся за dest
  • swap: биржами содержание своих аргументов

С этими двумя простыми и безопасными примитивами вы можете избежать пробивания отверстий в своих данных.

Смежные вопросы