Почему я не могу вызвать метод с временным значением? - PullRequest
0 голосов
/ 17 января 2019

Я не могу позвонить Foo::new(words).split_first() в следующем коде

fn main() {
    let words = "Sometimes think, the greatest sorrow than older";
/*
    let foo = Foo::new(words);
    let first = foo.split_first();
*/

    let first = Foo::new(words).split_first();

    println!("{}", first);
}

struct Foo<'a> {
    part: &'a str,
}

impl<'a> Foo<'a> {

    fn split_first(&'a self) -> &'a str {
        self.part.split(',').next().expect("Could not find a ','")
    }

    fn new(s: &'a str) -> Self {
        Foo { part: s }
    }
}

компилятор выдаст мне сообщение об ошибке

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:8:17
   |
8  |     let first = Foo::new(words).split_first();
   |                 ^^^^^^^^^^^^^^^              - temporary value is freed at the end of this statement
   |                 |
   |                 creates a temporary which is freed while still in use
9  | 
10 |     println!("{}", first);
   |                    ----- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

Если я сначала свяжу значение Foo::new(words), а затем вызову метод split_first, проблем не будет.

Эти два метода вызова должны быть интуитивно одинаковыми, но как-то разными.

Ответы [ 2 ]

0 голосов
/ 17 января 2019

Краткий ответ: удалить 'a время жизни для параметра self split_first: fn split_first(&self) -> &'a str ( детская площадка ).

Длинный ответ:

Когда вы пишете этот код:

struct Foo<'a> {
    part: &'a str,
}

impl<'a> Foo<'a> {
    fn new(s: &'a str) -> Self {
        Foo { part: s }
    }
}

Вы говорите компилятору, что все экземпляры Foo связаны с некоторым временем жизни 'a, которое должно быть равно или короче времени жизни строки, передаваемой в качестве параметра Foo::new. Это время жизни 'a может отличаться от времени жизни каждого экземпляра Foo. Когда ты тогда пишешь:

let words = "Sometimes think, the greatest sorrow than older";
Foo::new(words)

Компилятор делает вывод, что время жизни 'a должно быть равно или меньше времени жизни words. За исключением любых других ограничений, компилятор будет использовать время жизни words, которое равно 'static, поэтому оно действует в течение всего срока службы программы.

Когда вы добавляете ваше определение split_first:

fn split_first(&'a self) -> &'a str

Вы добавляете дополнительное ограничение: вы говорите, что 'a также должно быть равно или короче, чем время жизни self. Поэтому компилятор примет меньшее время жизни words и время жизни временного экземпляра Foo, то есть время жизни временного экземпляра. @ Ответ AndersKaseorg объясняет, почему это не работает.

Удаляя время жизни 'a для параметра self, я декоррелирую 'a от времени жизни временного, поэтому компилятор может снова сделать вывод, что 'a - это время жизни words, которое достаточно долго для работы программы.

0 голосов
/ 17 января 2019

Foo::new(words).split_first() будет интерпретироваться примерно как

let tmp = Foo::new(words);
let ret = tmp.split_first();
drop(tmp);
ret

Если бы Rust позволил вам сделать это, ссылки в ret указали бы [править: , будет разрешено типом split_first указывать *] в теперь упало значение tmp. Так что хорошо, что Руст запрещает это. Если бы вы написали эквивалентную однострочную версию в C ++, вы бы молча получили неопределенное поведение.

Пишя привязку let самостоятельно, вы задерживаете отбрасывание до конца области, тем самым расширяя область, где эти ссылки можно безопасно хранить.

Подробнее см. временные времена жизни в справочнике по ржавчине.

* Редактировать: Как указывает Jmb , реальная проблема в этом конкретном примере заключается в том, что тип

fn split_first(&'a self) -> &'a str

недостаточно конкретен, и лучшим решением будет уточнить тип до:

fn split_first<'b>(&'b self) -> &'a str

которое может быть сокращено:

fn split_first(&self) -> &'a str

Это подразумевает гарантию того, что возвращаемые ссылки не будут указывать на Foo<'a> (только на саму строку).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...