Этот пост предназначен для людей, которые только начали работать с Rust и не совсем понимают, как работает заимствование и пожизненное использование.
TL; DR После того, как что-то было перемещено, оно исчезло. Только одна изменяемая ссылка за раз. Копирование может быть дорогостоящим, но иногда требуется.
Я запустил небольшой HTTP-сервер, чтобы побольше узнать о Rust. Выполняя первые несколько шагов, я всегда сталкивался с одними и теми же ошибками и каким-то образом умудрялся их обойти. Это было не очень приятно.
Перечитывая некоторые главы книги Rust о заимствованиях и сроках жизни, я наконец понял это. Я попытаюсь объяснить свое мнение о том, как это работает. Может, тебе это тоже поможет.
Заимствование и переезд
Каждый раз, когда вы создаете переменную и используете ее в качестве аргумента функции, вы перемещаете эту переменную. После того, как переменная была перемещена, вы не можете использовать ее снова.
fn main() { let foo = String::from("Hi"); other_function(foo); // foo is now gone, the ownership was moved }
Однако, если бы мы сделали копию нашей переменной, это сработало бы. Также было бы работать, если бы тип нашей переменной был просто целым числом, которое по умолчанию будет скопировано, а не перемещено.
использованная литература
Если мы не хотим копировать наши переменные для экономии памяти или нам это нужно позже в нашей области видимости, мы можем вместо этого использовать ссылки.
fn main() { let foo = String::from("Hi"); do_something(&foo); do_more(&foo); }
Вы можете делать это так часто, как хотите. Единственное, на что вам нужно обратить внимание, - это изменяемые ссылки. Вы можете создавать только одну изменяемую ссылку за раз.
// you cannot do this let mut foo = String::from("Hi"); let x = &mut foo; let y = &foo; // you cannot do this let mut foo = String::from("Hi"); let x = &mut foo; let y = &foo; // but you can do this let mut foo = String::from("Hi"); do_something(&mut foo); do_more(&mut foo);
Последний работает, потому что функции будут вызываться одна за другой. Некогда существовало две ссылки.
Возврат ссылки
Иногда я пытался вернуть ссылку на переменную, созданную мной в той же области. Это, конечно, вызывает ошибку, поскольку вы не можете вернуть ссылку на то, чего больше не существует. Чтобы это сработало, вам нужно скопировать значение.
fn main() { let string1 = String::from("Hi"); let string2 = String::from("Hello"); let longer = longer_string(&string1, &string2); println!("Result: {:?}", longer); } fn longer_string(a: &String, b: &String) -> String { if a.len() > b.len() { a.clone() } else { b.clone() } }
Это не вернет ссылку, а будет возвращено значение типа String, которое затем можно будет использовать в области, которая вызвала эту функцию.
Время жизни
Время жизни необходимо, если у вас есть функция, которая возвращает ссылку или требует ее в качестве аргумента. Иногда компилятор знает время жизни, но в большинстве случаев вам придется указывать его.
fn main() { let foo = String::from("Hi"); let c = first_letter(&foo); println!("{:?}", c); } fn first_letter<'a>(bar: &'a String) -> &'a str { return &bar[0..1]; }
Время жизни в этом примере не требуется, поскольку он прост и компилятор понимает простые вещи. Однако ключевым моментом здесь является то, что время жизни сообщает компилятору, что ссылки, используемые в этой функции, будут жить достаточно долго. В этом случае, пока ‘a.
fn main() { let string1 = String::from("Hi"); { let string2 = String::from("Hello"); let longer = longer_string(&string1, &string2); println!("Result: {:?}", longer); } } fn longer_string<'a>(a: &'a String, b: &'a String) -> &'a str { return if a.len() > b.len() { a } else { b }; }
Здесь Rust требует указания времени жизни. Аргументы поступают из двух разных областей, поэтому только самый короткий из них является допустимым временем жизни, которое может быть возвращено.
Вывод
Я пропустил некоторые части, чтобы сделать это кратко. Для получения дополнительной информации прочтите Книгу Rust или загляните в документацию.