Будь ты проклят, если да, будь ты проклят, если нет: компилятор Rust жалуется независимо от того, есть ли параметр времени жизни или нет

#rust #lifetime

Вопрос:

Я пытаюсь решить, следует ли мне добавить параметр lifetime в мои impl, но, похоже, я нахожусь в ситуации «будь я проклят, если ты это сделаешь, будь я проклят, если ты этого не сделаешь», потому что компилятор жалуется независимо от того, есть параметр lifetime или нет.

 pub struct TurtleRef<'a> {
    t: amp;'a BorrowedTurtle<'a>,
}

impl TurtleRef<'_> {
    pub fn borrowed_turtle(amp;self) -> BorrowedTurtle {
        *self.t
    }

    pub fn new(r: Turtle) -> TurtleRef {
        TurtleRef{t: amp;BorrowedTurtle{ t:r}}
    }
}

pub struct BorrowedTurtle<'a> {
    t: Turtle<'a>,
}

impl<'a> std::ops::Deref for BorrowedTurtle<'_> {
    type Target = Turtle<'a>;

    fn deref(amp;self) -> amp;Self::Target {
        amp;self.t
    }
}

impl<'a> std::ops::DerefMut for BorrowedTurtle<'_> {
    type Target = Turtle<'a>;
    fn deref_mut(amp;mut self) -> amp;mut Self::Target {
        amp;self.t
    }
}

pub struct Turtle<'a> {
    children: Vec<Turtle<'a>>,
}
 

У черепахи больше полей, но я удалил их для простоты. Вы можете увидеть фрагмент кода здесь. Код выдает ошибку

 error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
  --> src/campus.rs:54:6
   |
54 | impl<'a> std::ops::Deref for BorrowedTurtle<'_> {
   |      ^^ unconstrained lifetime parameter
 

Нет проблем, я просто удалю параметр, так как он вызывает такую суету. Но после его удаления я получаю целую кучу новых ошибок:

 
error[E0261]: use of undeclared lifetime name `'a`
  --> src/campus.rs:55:26
   |
55 |     type Target = Turtle<'a>;
   |                          ^^ undeclared lifetime
   |
help: consider introducing lifetime `'a` here
   |
54 | impl<'a> std::ops::Deref for BorrowedTurtle<'_> {
   |     ^^^^
help: consider introducing lifetime `'a` here
   |
55 |     type Target<'a> = Turtle<'a>;
   |                ^^^^
 

Что бы вы ни сказали, я просто продолжу и добавлю этот параметр в target. Но теперь я получаю еще одну ошибку:

 error[E0658]: generic associated types are unstable
  --> src/campus.rs:55:5
   |
55 |     type Target<'a> = Turtle<'a>;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
 

Поэтому, что бы я ни делал, похоже, я столкнулся с серьезной ошибкой. Как мне остановить эти ошибки, не начиная все с нуля? Я хотел бы сохранить impl, структуры и функции deref, поэтому единственное, что я должен изменить, — это их реализацию.

На другой заметке я получаю ошибку

 error[E0437]: type `Target` is not a member of trait `std::ops::DerefMut`
  --> src/campus.rs:64:5
   |
64 |     type Target = Turtle<'a>;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `std::ops::DerefMut`
 

потому что Turtle не реализует разыменование, и на самом деле Turtle не должна реализовывать разыменование. Есть ли небольшая модификация Turtle, которая приводит к чему-то, что уже реализует разыменование?

Комментарии:

1. @Stargateur 2 структуры и 3 импликации, которые я опубликовал, зависят друг от друга. Есть много функций, связанных с черепахой, которые я опустил, чтобы минимизировать код. DerefMut и Deref полагаются на заимствованный Turtle, на который полагаются TurtleRef и impl TurtleRef.

2. @Stargateur Но я понимаю, что вы имеете в виду насчет воспроизводимости, поэтому я добавил часть структуры Черепахи. Вы должны быть в состоянии увидеть все 3 ошибки, которые меня сейчас беспокоят: play.rust-lang.org/…

Ответ №1:

Здесь есть пара проблем. Во-первых:

 error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
  --> src/campus.rs:54:6
   |
54 | impl<'a> std::ops::Deref for BorrowedTurtle<'_> {
   |      ^^ unconstrained lifetime parameter
 

Вы либо используете анонимную жизнь, либо нет. Здесь вы объявляете 'a , так что используйте его:

 impl<'a> std::ops::Deref for BorrowedTurtle<'a> {
 

Чтобы использовать скрытое время жизни, вам не нужно его объявлять:

 impl std::ops::Deref for BorrowedTurtle<'_> {
 

Однако здесь вы должны ссылаться на время жизни Target , поэтому вы не можете его исключить.

Второй:

 error[E0437]: type `Target` is not a member of trait `std::ops::DerefMut`
  --> src/lib.rs:28:5
   |
28 |     type Target = Turtle<'a>;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `std::ops::DerefMut`
 

DerefMut у него нет Target члена, потому что он повторно использует его из своей супертрайты, Deref . Это делается для того, чтобы гарантировать, что предметы должны Deref и DerefMut к тому же Target :

 impl<'a> std::ops::Deref for BorrowedTurtle<'a> {
    type Target = Turtle<'a>;

    fn deref(amp;self) -> amp;Self::Target {
        amp;self.t
    }
}

impl std::ops::DerefMut for BorrowedTurtle<'_> {
    fn deref_mut(amp;mut self) -> amp;mut Self::Target {
        amp;mut self.t
    }
}
 

Наконец, теперь вы получите ошибки, которые 'a не используются:

 error[E0392]: parameter `'a` is never used
  --> src/lib.rs:15:27
   |
15 | pub struct BorrowedTurtle<'a> {
   |                           ^^ unused parameter
   |
   = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
 

Это связано с тем, что у вас есть несколько рекурсивных типов, в которых время жизни на самом деле не используется:

 // 'a isn't actually used for anything

pub struct Turtle<'a> {
    children: Vec<Turtle<'a>>,
}

pub struct BorrowedTurtle<'a> {
    t: Turtle<'a>,
}
 

Я предполагаю , что для целей этого ответа вы опустили другие соответствующие поля, в которых используется 'a , так что это все!

Комментарии:

1. Кто знал, что написание кода без продолжительности жизни, последующая многократная перекомпиляция и следование рекомендациям по продолжительности жизни, перечисленным после ошибок, вызовут такие проблемы. Не могу поверить, что я пропустил, что DerefMut-это особый тип Deref (это так же, как подклассы Java), но анонимные жизни были вызваны моей небрежностью и добавлением их в соответствии с рекомендациями компилятора.

Ответ №2:

Поскольку DerefMut наследовать от Deref , вам не нужно указывать Target , pub trait DerefMut: Deref { используйте Target определение в Deref реализации.

Черта Deref очень особенная, ее действительно не может использовать «обычный» пользователь, почти только std может реализовать ее для нового типа.

Это связано с тем, что он заимствует себя и возвращает ссылку на «что-то другое» проблема в том, что это что-то другое не может быть временным, std использует его контроль над экосистемой rust, чтобы иметь возможность сделать это, например Vec , реализация:

 impl<T, A: Allocator> ops::DerefMut for Vec<T, A> {
    fn deref_mut(amp;mut self) -> amp;mut [T] {
        unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) }
    }
}
 

как вы можете видеть, slice-это «жирный указатель», поэтому для его корректности не требуется ничего, кроме ссылки, вы можете реализовать почти только Deref для возврата Target типа like slice .

Другой пример-для PathBuf :

 impl ops::Deref for PathBuf {
    type Target = Path;
    #[inline]
    fn deref(amp;self) -> amp;Path {
        Path::new(amp;self.inner)
    }
}
 

Здесь еще яснее Path::new создать жирный указатель. Таким образом, целью не может быть что-то другое, что не может быть самодостаточным или уже существовать в вашем Self .

Как сказано в документации:

Из-за этого Deref следует реализовывать только для интеллектуальных указателей, чтобы избежать путаницы.

Я думаю, что то, что вы действительно хотите, — это реализовать Borrow . Все, что сказано… вот рабочий код:

 impl<'a> std::ops::Deref for BorrowedTurtle<'a> {
    type Target = Turtle<'a>;

    fn deref(amp;self) -> amp;Self::Target {
        amp;self.t
    }
}

impl std::ops::DerefMut for BorrowedTurtle<'_> {
    fn deref_mut(amp;mut self) -> amp;mut Self::Target {
        amp;mut self.t
    }
}
 

Делай, как хочешь.