Почему я не могу брать &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 так, чтобы он обслуживал ту или иную вещь: ссылки или собственные значения. Ваш подходит для принадлежащих значений, поскольку у не-CopyT также есть та же «проблема», что вы не можете вызвать 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без комментариев. Разве в этом случае не должно произойти сбой без пожизненных аннотаций?