Как мне извлечь элемент из Vec, который перемещен в структуру?

#rust #borrow-checker

#Ржавчина #проверка заимствования

Вопрос:

У меня есть структура, которая содержит вектор структур, например

 fn main() {
    let x: Vec<Item> = Vec::new(); 
    // assume x is filled with stuff
    do_things_with(x);
}

struct Item {
    value: String,
}

struct Context {
    x: Vec<Item>,
}

impl Context {
    fn get(amp;mut self, at: usize) -> Item {
        self.x[at]
    }
}

fn do_things_with(x: Vec<Item>) {
    let mut ctx = Context{
        x: x,
    };
    ctx.get(5);
}
  

У меня есть Vec с материалом, и я передаю это некоторой функции, которая создает контекст и сохраняет переданное значение в этой структуре. Затем я хочу просмотреть элементы в этом Vec, чтобы у меня были некоторые вспомогательные функции, например ‘get’, которые получат элемент по указанному индексу.

Кажется, все хорошо, и на C или любом другом языке это было бы прекрасно, однако Rust жалуется:

 'cannot move out of borrowed content'
  

Для функции ‘get’, где мы пытаемся получить доступ к элементу в векторе.

Что я здесь делаю не так?

Ответ №1:

Проблема здесь в том, что Vec имеет право собственности на Item содержащиеся в нем файлы, но Context.get пытается вернуть Item их напрямую (и завладеть ими).

Если Context.get просто нужно позволить вызывающим пользователям просматривать содержимое вектора, он должен возвращать ссылку на элемент вместо элемента:

 impl Context {
    fn get(amp;mut self, at: usize) -> amp;Item {
        amp;self.x[at]
    }
}
  

В приведенном выше случае Context.get можно использовать неизменяемую ссылку на self , поскольку это ничего не меняет. Кроме того, если вы хотите разрешить вызывающим из Context.get изменять элемент, на который ссылается ссылка, вы должны вернуть amp;mut Item вместо amp;Item :

 impl Context {
    fn get(amp;mut self, at: usize) -> amp;mut Item {
        amp;mut self.x[at]
    }
}
  

Редактировать: Как @apemanzilla услужливо отмечает в комментариях, вы также могли бы заставить Item реализовать Clone признак, если вам нужен Context.get для возврата отдельной копии элемента в at :

 #[derive(Clone)]
struct Item {
    value: String,
}

impl Context {
    fn get(amp;mut self, at: usize) -> Item {
        self.x[at].clone()
    }
  

Однако изменение возвращаемого элемента не приведет к изменению элемента, содержащегося в self.x; это может быть или не быть тем, что вы предполагали.

Комментарии:

1. Я бы также добавил примечание об использовании признака Clone или Copy для небольших типов.

2. Это идеально, спасибо. Что предпочтительнее: создание клона чего-либо или неизменяемое заимствование этого. Обратите внимание, что в контексте элемент не будет изменен.

3. @flooblebit Клонирование потенциально дорого. Вы должны клонировать, если ваше намерение таково: «Я хочу отдельную копию этой вещи, которую я могу модифицировать без изменения оригинала». Если вы не меняете оригинал, предпочитайте неизменяемые ссылки; если вы меняете оригинал, предпочитайте изменяемую ссылку.