Средство проверки заимствования и &mut заимствует для общих параметров

Почему я не могу брать &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);
    }
}
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Давайте дадим имена временам жизни (используя воображаемый синтаксис):

#[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.

Спасибо за ответ. Итак, если я правильно понимаю, основная причина в том, что компилятор предоставил экземпляру T, используемому в is_mut_t скрытую жизнь (которая отличается от self)? Я попробовал следующие изменения: fn is_mut_t(&mut self, i: &mut T) {} и затем let mut t: Test<i32> = Test::new();. В таком случае все компилируется с вызовами is_mut_t без комментариев. Разве в этом случае не должно произойти сбой без пожизненных аннотаций?

Hassan Syed 26.06.2024 21:17

@HassanSyed Опять же, замените T на какой-нибудь тип. Если этот тип содержит время жизни, оно должно быть таким же. Если время жизни в методе (исключено), оно заменяется общим временем жизни.

Chayim Friedman 26.06.2024 21:23

@HassanSyed еще один способ взглянуть на это: 'd, 'j и 'l должны быть одинаковыми по времени жизни (потому что T равно &'d mut i32 согласно объявлению t, а также равно &'j mut i32 и 'l mut i32 для двух вызовов is_mut_t). Когда вы удаляете ссылку из T, вы разрываете связь между сроками жизни, позволяя компилятору выбирать 'j != 'l.

Jmb 27.06.2024 09:30

У вас уже есть ответ на ваш прямой вопрос - что 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()), но только более ручным способом.

Другие вопросы по теме