Структура Rust может занять "&" mut self "дважды, так почему бы не отличиться?
Следующий код Rust успешно компилируется:
struct StructNothing;
impl<'a> StructNothing {
fn nothing(&'a mut self) -> () {}
fn twice_nothing(&'a mut self) -> () {
self.nothing();
self.nothing();
}
}
Однако, если мы попытаемся упаковать его в черту, он потерпит неудачу:
pub trait TraitNothing<'a> {
fn nothing(&'a mut self) -> () {}
fn twice_nothing(&'a mut self) -> () {
self.nothing();
self.nothing();
}
}
Это дает нам:
error[E0499]: cannot borrow '*self' as mutable more than once at a time
--> src/lib.rs:6:9
|
1 | pub trait TraitNothing<'a> {
| -- lifetime ''a' defined here
...
5 | self.nothing();
| --------------
| |
| first mutable borrow occurs here
| argument requires that '*self' is borrowed for ''a'
6 | self.nothing();
| ^^^^ second mutable borrow occurs here
- Почему разрешена первая версия, а вторая запрещена?
- Есть ли способ убедить компилятор, что вторая версия в порядке?
Фон и мотивация
Такие библиотеки, как rust-csv
хотели бы поддерживать потоковый анализ и анализ с нулевым копированием, потому что это в 25-50 раз быстрее, чем выделение памяти (согласно тестам). Но встроенная черта Rust Iterator
не может быть использована для этого, потому что нет способа реализовать collect()
. Цель состоит в том, чтобы определить признак StreamingIterator
который может использоваться совместно с rust-csv
и несколькими подобными библиотеками, но каждая попытка реализовать его до сих пор сталкивалась с проблемой, описанной выше.
Ответы
Ответ 1
Следующее является расширением ответа Фрэнсиса с использованием неявных времен жизни, но оно позволяет возвращаемому значению быть ограниченным временем жизни:
pub trait TraitNothing<'a> {
fn change_it(&mut self);
fn nothing(&mut self) -> &Self {
self.change_it();
self
}
fn bounded_nothing(&'a mut self) -> &'a Self {
self.nothing()
}
fn twice_nothing(&'a mut self) -> &'a Self {
// uncomment to show old fail
// self.bounded_nothing();
// self.bounded_nothing()
self.nothing();
self.nothing()
}
}
Это менее чем идеально, но вы можете вызывать методы с неявным временем жизни change_it
и nothing
несколько раз в других методах. Я не знаю, решит ли это вашу настоящую проблему, потому что в конечном итоге self
имеет общий тип &mut Self
в методах trait, тогда как в структуре он имеет тип &mut StructNothing
и компилятор не может гарантировать, что Self
не содержит ссылку. Этот обходной путь действительно решает пример кода.
Ответ 2
Если вы поместите параметры времени жизни в каждый метод, а не в сам признак, он скомпилирует:
pub trait TraitNothing {
fn nothing<'a>(&'a mut self) -> () {}
fn twice_nothing<'a>(&'a mut self) -> () {
self.nothing();
self.nothing();
}
}
Ответ 3
Это действительно удивительно?
Утверждение, которое вы делаете, заключается в том, что &mut self
длится, по крайней мере, всю жизнь 'a
.
В первом случае &mut self
является указателем на структуру. Псевдоним указателя не происходит, потому что заимствование полностью содержится в nothing()
.
В последнем случае &mut self
является указателем на указатель на структуру + vtable для признака. Вы блокируете указатель на структуру, которая реализует TraitNothing
на время 'a
; т.е. вся функция каждый раз.
Удаляя 'a
, вы неявно используете 'static
, который говорит, что impl длится вечно, так что все в порядке.
Если вы хотите обойти это, преобразуйте &'static TraitNothing
от &'a TraitNothing
to &'static TraitNothing
... но я уверен, что это не то, что вы хотите сделать.
Вот почему нам нужны области видимости блоков ('b: {.... }
) в Rust...
Попробуйте использовать пустышки?