TL; DR: ответ Нет , я не могу.
После обсуждений с @Peter Hall и @Stargateur я понял, почему мне нужно использовать&mut Option<&mut Thing>
везде.RefCell<>
также будет возможным обходным путем, но он не будет аккуратнее и не будет действительно соответствовать шаблону, который я изначально стремился реализовать.
Проблема заключается в следующем: если разрешить мутировать объект длякоторый имеет только неизменную ссылку на Option<&mut T>
, можно использовать эту власть, чтобы полностью нарушить правила заимствования.Конкретно, вы можете, по сути, иметь много изменяемых ссылок на один и тот же объект, потому что вы можете иметь много таких неизменных ссылок.
Я знал была только одна изменяемая ссылка на Thing
(принадлежащая Option<>
), но, как только я начал брать ссылки на Option<>
, компилятор больше не знал, что там не быломногие из них.
Лучшая версия шаблона выглядит следующим образом:
fn try_increment(handle: &mut Option<&mut Thing>) -> bool {
if let Some(ref mut t) = handle {
t.increment_self();
true
}
else {
println!("warning: increment failed");
false
}
}
fn try_increment_twice(mut handle: Option<&mut Thing>) {
try_increment(&mut handle);
try_increment(&mut handle);
}
fn main() {
try_increment_twice(None);
let mut thing = Thing(0);
try_increment_twice(Some(&mut thing));
try_increment_twice(None);
}
Примечания:
-
Option<>
содержит единственную существующую изменяемую ссылкуThing
try_increment_twice()
вступает во владение Option<>
try_increment()
должен принять Option<>
как &mut
, так что компиляторзнает, что он имеет единственную изменяемую ссылку на Option<>
во время вызова - Если компилятор знает, что
try_increment()
имеет единственную изменяемую ссылку на Option<>
, которая содержит уникальную изменяемую ссылку на Thing
, компилятор знает, что правила заимствования не были нарушены.
Другой эксперимент
Проблема изменчивости Option<>
остается, потому что можно позвонить take()
и соавт.на изменяемом Option<>
, нарушая все следующее.
Чтобы реализовать шаблон, который я хотел, мне нужно что-то, что подобно Option<>
, но даже если оно изменяемый , он не может быть изменен.Примерно так:
struct Handle<'a> {
value: Option<&'a mut Thing>,
}
impl<'a> Handle<'a> {
fn new(value: &'a mut Thing) -> Self {
Self {
value: Some(value),
}
}
fn empty() -> Self {
Self {
value: None,
}
}
fn try_mutate<T, F: Fn(&mut Thing) -> T>(&mut self, mutation: F) -> Option<T> {
if let Some(ref mut v) = self.value {
Some(mutation(v))
}
else {
None
}
}
}
Теперь я подумал, что могу обходить &mut Handle
весь день и знать, что тот, у кого есть Handle
, может изменять только его содержимое, а не сам дескриптор,( См. Playground )
К сожалению, даже это ничего не дает, потому что, если у вас есть изменяемая ссылка, вы всегда можете переназначить ее с помощью оператора разыменования:
fn try_increment(handle: &mut Handle) -> bool {
if let Some(_) = handle.try_mutate(|t| { t.increment_self() }) {
// This breaks future calls:
(*handle) = Handle::empty();
true
}
else {
println!("warning: increment failed");
false
}
}
Что все хорошо и хорошо.
Итог: просто используйте &mut Option<&mut T>