#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
}
}
Делай, как хочешь.