#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 Клонирование потенциально дорого. Вы должны клонировать, если ваше намерение таково: «Я хочу отдельную копию этой вещи, которую я могу модифицировать без изменения оригинала». Если вы не меняете оригинал, предпочитайте неизменяемые ссылки; если вы меняете оригинал, предпочитайте изменяемую ссылку.