Почему я не могу брать &mut x
несколько раз при использовании is_mut_t
. Но с is_mut_specific
все в порядке?
#[cfg(test)]
mod test {
use std::marker::PhantomData;
#[test]
fn test_wc() {
struct Test<T>{ phantom_data: PhantomData<T>}
impl <T> Test<T> {
fn new() -> Self {
Test { phantom_data: PhantomData }
}
fn is_mut_t(&mut self, i: T) {}
fn is_mut_specific(&mut self, i: &mut i32) {}
}
let mut x = 5;
let mut t: Test<&mut i32> = Test::new();
t.is_mut_specific(&mut x);
t.is_mut_specific(&mut x);
// t.is_mut_t(&mut x);
// t.is_mut_t(&mut x);
}
}
Давайте дадим имена временам жизни (используя воображаемый синтаксис):
#[cfg(test)]
mod test {
use std::marker::PhantomData;
#[test]
fn test_wc() {
struct Test<T> { phantom_data: PhantomData<T> }
impl<T> Test<T> {
fn new() -> Self {
Test { phantom_data: PhantomData }
}
fn is_mut_t<'a>(&'a mut self, i: T) {}
fn is_mut_specific<'b, 'c>(&'b mut self, i: &'c mut i32) {}
}
let mut x = 5;
let mut t: Test<&'d mut i32> = Test::new();
(&'e mut t).is_mut_specific::<'e, 'f>(&'f mut x);
(&'g mut t).is_mut_specific::<'g, 'h>(&'h mut x);
// (&'i mut t).is_mut_t::<'i>(&'j mut x);
// (&'k mut t).is_mut_t::<'k>(&'l mut x);
}
}
Каждое появление T
заменяется на &'d mut i32
. Можете ли вы обнаружить проблему?
Когда мы говорим &mut i32
в подписи, ему назначается общее время жизни, которое может быть любым.
Но когда мы говорим T
, это должно быть точно так же, как T
. И это включает в себя ту же жизнь. Но если мы одолжим x
для 'd
, мы не сможем снова одолжить его в 'd
, что означает, по крайней мере, до тех пор, пока существует t
.
@HassanSyed Опять же, замените T
на какой-нибудь тип. Если этот тип содержит время жизни, оно должно быть таким же. Если время жизни в методе (исключено), оно заменяется общим временем жизни.
@HassanSyed еще один способ взглянуть на это: 'd
, 'j
и 'l
должны быть одинаковыми по времени жизни (потому что T
равно &'d mut i32
согласно объявлению t
, а также равно &'j mut i32
и 'l mut i32
для двух вызовов is_mut_t
). Когда вы удаляете ссылку из T
, вы разрываете связь между сроками жизни, позволяя компилятору выбирать 'j != 'l
.
У вас уже есть ответ на ваш прямой вопрос - что T
as &mut i32
имеет неявное время жизни, которое используется для всех значений T
, что в вашем коде будет означать несколько изменяемых ссылок одновременно - однако я хотел бы предложить потенциальный вариант обходной путь.
Часто предпочтительнее спроектировать свой API так, чтобы он обслуживал ту или иную вещь: ссылки или собственные значения. Ваш подходит для принадлежащих значений, поскольку у не-Copy
T
также есть та же «проблема», что вы не можете вызвать is_mut_t
дважды по одному и тому же значению (поскольку оно было перемещено при первом вызове).
Однако если вы хотите сохранить этот дизайн, но при этом сохранить некоторую гибкость в отношении ссылок, вы можете ввести метод «перезаимствования», чтобы сократить общий срок службы. Что-то вроде этого:
impl<'a, T> Test<&'a mut T> {
fn reborrow_mut(&mut self) -> Test<&'_ mut T> {
Test { phantom_data: PhantomData }
}
}
let mut x = 5;
let mut t: Test<&mut i32> = Test::new();
t.reborrow_mut().is_mut_t(&mut x);
t.reborrow_mut().is_mut_t(&mut x);
Этот метод позволяет вам создать новый Test<&mut T>
с более коротким сроком жизни, полученный из &self
, который компилятор может сжать до простого вызова метода. Это имитирует способность компилятора неявно перезаимствовать ссылки (как это делается для вызовов .is_mut_specific()
), но только более ручным способом.
Спасибо за ответ. Итак, если я правильно понимаю, основная причина в том, что компилятор предоставил экземпляру
T
, используемому вis_mut_t
скрытую жизнь (которая отличается от self)? Я попробовал следующие изменения:fn is_mut_t(&mut self, i: &mut T) {}
и затемlet mut t: Test<i32> = Test::new();
. В таком случае все компилируется с вызовамиis_mut_t
без комментариев. Разве в этом случае не должно произойти сбой без пожизненных аннотаций?