Это очень широкий вопрос.Средство проверки заимствований является, пожалуй, одной из самых полезных функций Rust, но в то же время и самой хитрой.Улучшения в эргономике делаются регулярно, но иногда такие ситуации случаются.
Есть несколько способов справиться с этим, и я постараюсь просмотреть все плюсы и минусы каждого:
I,Преобразование в форму, которая требует только ограниченного заимствования
По мере изучения Руста вы медленно узнаете, когда истекает срок заимствований и как быстро.В этом случае, например, вы можете преобразовать в
if context.get_name() == "foo" {
context.set_foo(4);
}
Срок действия заимствования истекает в операторе if.Этот обычно - это путь, по которому вы хотите идти, и по мере того, как улучшаются такие функции, как нелексические времена жизни, эта опция становится более приемлемой.Например, способ, которым вы сейчас написали, будет работать, когда NLL будут доступны, потому что эта конструкция должным образом определена как «ограниченный заем»!Переформулировка иногда завершается неудачей по странным причинам (особенно если для оператора требуется соединение изменяемых и неизменных вызовов), но это должен быть ваш первый выбор.
II.Используйте взламывание областей действия с выражениями как операторы
let name_is_foo = {
let name = context.get_name();
name == "foo"
};
if name_is_foo {
context.set_foo(4);
}
Способность Rust использовать произвольно ограниченные операторы, которые возвращают значения, является невероятно мощным.Если все остальное терпит неудачу, вы можете почти всегда использовать блоки для ограничения своих заимствований и возвращать только значение флага без заимствования, которое вы затем используете для своих изменяемых вызовов.Обычно понятнее использовать метод I. , когда он доступен, но этот полезен, понятен и идиоматичен. Rust.
III.Создайте «метод слияния» для типа
impl Context {
fn set_when_eq(&mut self, name: &str, new_foo: i32) {
if self.name == name {
self.foo = new_foo;
}
}
}
Конечно, есть бесконечные варианты этого.Самым общим является функция, которая принимает fn(&Self) -> Option<i32>
и устанавливает на основе возвращаемого значения этого замыкания (None
для "not set", Some(val)
для установки этого значения).
Иногда лучше позволить структуре изменять себя, не делая логику «снаружи».Это особенно верно для деревьев, но в худшем случае может привести к взрыву метода, и, конечно, это невозможно, если вы работаете с чужим типом, который вы не контролируете.
IV.Клон
let name = context.get_name().clone();
if name == "foo" {
context.set_foo(4);
}
Иногда нужно сделать быстрый клон.Избегайте этого, когда это возможно, но иногда стоит просто куда-то добавить clone()
вместо того, чтобы тратить 20 минут, пытаясь выяснить, как, черт возьми, заставить ваши кредиты работать.Зависит от вашего крайнего срока, того, насколько дорогим является клон, как часто вы вызываете этот код и т. Д.
Например, возможно чрезмерное клонирование PathBuf
с в приложениях CLI не является ужасно редким явлением.
V.Используйте unsafe ( НЕ РЕКОМЕНДУЕТСЯ )
let name: *const str = context.get_name();
unsafe{
if &*name == "foo" {
context.set_foo(4);
}
}
Это почти никогда не должно использоваться, но может быть необходимо в крайних случаях или для производительности в тех случаях, когда вы фактически вынуждены клонировать (это может случиться с графиками или некоторыми сложными структурами данных)Всегда старайтесь изо всех сил избегать этого, но держите его в своем наборе инструментов на случай, если вам абсолютно необходимо.
Имейте в виду, что компилятор ожидает, что небезопасный код, который вы пишете, поддерживает все гарантии, необходимые для безопасного кода Rust.Блок unsafe
указывает, что, хотя компилятор не может проверить, что код безопасен, программист имеет.Если программист не правильно проверил это, компилятор, скорее всего, создаст код, содержащий неопределенное поведение, которое может привести к небезопасной памяти, сбоям и т. Д., Многим из которых Rust стремится избежать.