Пожизненная обработка при возврате внутренней ссылки из ссылки, переданной в качестве аргумента функции - PullRequest
0 голосов
/ 08 сентября 2018

По какой причине следующий код прекрасно компилируется, несмотря на то, что время жизни 'a и 'b не зависит друг от друга?

struct Foo<'a> {
    i: &'a i32
}

fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

fn main() {}

Если я сделаю ссылку i в Foo mutable, выдает следующую ошибку.

5 | fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
  |                    -----------     -------
  |                    |
  |                    this parameter and the return type are declared with different lifetimes...
6 |     x.i
  |     ^^^ ...but data from `x` is returned here

По какой причине выдается вышеуказанная ошибка ?.Считает ли он свое владение изменяемой ссылкой и видит, что что-то (из Foo) извлекается (с независимым временем жизни), что невозможно, отсюда и ошибка?

Этот код (который ямысль прошла бы) тоже терпит неудачу:

struct Foo<'a> {
    i: &'a mut i32
}

fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

fn main() {}

терпит неудачу с ошибкой:

 error[E0623]: lifetime mismatch
 --> src/main.rs:6:5
  |
5 | fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
  |                        -----------
  |                        |
  |                        these two types are declared with different lifetimes...
6 |     x.i
  |     ^^^ ...but data from `x` flows into `x` here

Но этот проход проходит:

struct Foo<'a> {
    i: &'a mut i32
}

fn func<'a: 'b, 'b>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

fn main() {}

Это кажется немного нелогичныммне.Здесь внешнее время жизни ('a) может пережить внутреннее время жизни ('b).Почему это не ошибка?

Ответы [ 3 ]

0 голосов
/ 09 сентября 2018

По какой причине следующий код прекрасно компилируется, несмотря на то, что время жизни 'a и 'b не зависят друг от друга?

fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

Причина в том, что они не независимы друг от друга.

Тип &'a Foo<'b> был бы невозможен, если бы 'a пережил 'b. Таким образом, неявно Rust заимствователь выводит, что вы должны иметь , что 'b: 'a ('b переживает 'a). Таким образом, приведенный выше код семантически такой же, как этот:

fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

Если я сделаю ссылку i в Foo изменяемой, она выдаст следующую ошибку.

5 | fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
  |                    -----------     -------
  |                    |
  |                    this parameter and the return type are declared with different lifetimes...
6 |     x.i
  |     ^^^ ...but data from `x` is returned here

По какой причине выдается вышеуказанная ошибка?

Когда вы передаете ссылку неизменный , он получает скопированный . В приведенном выше примере это означает, что &'b i32 можно перемещать самостоятельно, и его жизнеспособность не связана с тем, откуда вы его взяли. Эта скопированная ссылка всегда указывает на исходный адрес данных. И именно поэтому первый пример работает - даже если x был удален, исходная ссылка все равно будет действительной.

Когда вы передаете ссылку mutable , она перемещается на . Следствием этого является то, что ссылка все еще «через» переменную x. Если бы это было не так, изменяемая ссылка x на содержимое Foo могла бы существовать одновременно с этой новой неизменной ссылкой, что недопустимо. Таким образом, в этом случае ссылка не может пережить 'a - или поставить иначе: 'a: 'b.

Но разве мы уже не сказали 'b: 'a? Единственным выводом здесь является то, что 'a и 'b должны быть того же времени жизни , чего требовало ваше предыдущее сообщение об ошибке.

0 голосов
/ 10 сентября 2018

Просто, чтобы добавить предложение (см. Другие ответы для подробного объяснения вашего вопроса):

Всякий раз, когда возможно, не перегружайте с продолжительностью жизни.

В этомЯвный случай, когда речь идет обо всех жизнях, подразумевает 4 случая (и много размышлений!).

Дело 1 (компиляция)

fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'a i32 {
    x.i
}

Дело 2 (компиляция)

fn func<'a: 'b, 'b>(x: &'a Foo<'b>) -> &'a i32 {
    x.i
}

Дело 3 (компиляция)

fn func<'a: 'b, 'b>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

Дело 4(не компилировать)

fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

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

fn func<'a>(x: &'a Foo) -> &'a i32 {
    x.i
}
0 голосов
/ 09 сентября 2018

По какой причине выдается вышеуказанная ошибка? Считает ли это владение изменяемой ссылкой, и он видит что-то (из Foo) вынимается (с независимым временем жизни), который не возможно, отсюда и ошибка?

Изменяемый заем действительно не может быть перемещен из Foo, поскольку изменяемые заимствования не Copy. Неявно заимствовано:

fn func <'a, 'b> (x:&'a Foo<'b>) -> &'b i32 {
    &*x.i
}

Однако это не является источником проблемы. Во-первых, вот краткое изложение четырех версий func (struct Foo не имеет отношения к моему объяснению):

версия 1 (компилируется):

fn func<'a, 'b>(x: &'a &'b i32) -> &'b i32 {
    x
}

версия 2 (не компилируется):

fn func<'a, 'b>(x: &'a &'b mut i32) -> &'b i32 {
    x
}

версия 3 (не компилируется):

fn func<'a, 'b: 'a>(x: &'a &'b mut i32) -> &'b i32 {
    x
}

версия 4 (компилируется):

fn func<'a: 'b, 'b>(x: &'a &'b mut i32) -> &'b i32 {
    x
}

Версии 2 и 3 терпят неудачу, потому что они нарушают правило отсутствия псевдонимов, которое запрещает иметь изменяемую ссылку и неизменную ссылку на ресурс одновременно. В обеих версиях 'b может строго пережить 'a. Следовательно, &'b mut i32 и &'b i32 могут сосуществовать. Версия 1 компилируется, потому что правила псевдонимов допускают несколько неизменных ссылок на ресурс одновременно. Следовательно, &'b i32 может юридически сосуществовать с другим &'b i32.

На первый взгляд, похоже, что версия 4 должна потерпеть неудачу, поскольку снова возникают изменяемые заимствования и неизменные заимствования того же времени жизни. Разница между версиями 2 и 3 заключается в том, что на этот раз 'a живет по крайней мере столько же, сколько 'b из-за требования 'a: 'b, что означает, что 'b может не строго пережить 'a , Пока время жизни 'a длится, ссылочная i32 не может быть заимствована повторно во второй раз (изменяемые ссылки не Copy) - i32 уже зарезервировано заимствована для вызова func.

Вот пример, демонстрирующий, как версии 2 и 3 могут привести к неопределенному поведению:

fn func<'a, 'b: 'a>(x: &'a &'b mut String) -> &'b str {
    unsafe { std::mem::transmute(&**x as &str) } // force compilation
}

fn main() {
    let mut s = String::from("s");
    let mutref_s = &mut s;
    let ref_s = {
        let ref_mutref_s = &mutref_s;
        func(ref_mutref_s)
    };
    // use the mutable reference to invalidate the string slice       
    mutref_s.clear();
    mutref_s.shrink_to_fit();
    // use the invalidated string slice
    println!("{:?}", ref_s);
}

Замена версии 3 на версию 4 показывает, как в этом случае все еще активный внешний неизменяемый заем предотвращает второе изменяемое заимствование. Срок жизни 'a для внешнего неизменного заимствования вынуждается новым требованием 'a: 'b быть увеличенным до времени жизни 'b:

error[E0502]: cannot borrow `*mutref_s` as mutable because `mutref_s` is also borrowed as immutable
  --> src/main.rs:20:5
   |
17 |         let ref_mutref_s = &mutref_s;
   |                             -------- immutable borrow occurs here
...
20 |     mutref_s.clear();
   |     ^^^^^^^^ mutable borrow occurs here
...
23 | }
   | - immutable borrow ends here
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...