Подтвердить что ты не робот

Структура 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 и несколькими подобными библиотеками, но каждая попытка реализовать его до сих пор сталкивалась с проблемой, описанной выше.

4b9b3361

Ответ 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...

Попробуйте использовать пустышки?