Получение «временного значения, отброшенного при заимствовании» при попытке обновить параметр <& str> в цикле - PullRequest
0 голосов
/ 02 февраля 2019

Я пытаюсь реализовать часто используемый шаблон - используя результат предыдущей итерации цикла в следующей итерации цикла.Например, чтобы реализовать разбиение на страницы, где вам нужно указать идентификатор последнего значения на предыдущей странице.

struct Result {
    str: String,
}    

fn main() {
    let times = 10;
    let mut last: Option<&str> = None;

    for i in 0..times {
        let current = do_something(last);
        last = match current {
            Some(r) => Some(&r.str.to_owned()),
            None => None,
        };
    }
}

fn do_something(o: Option<&str>) -> Option<Result> {
    Some(Result {
        str: "whatever string".to_string(),
    })
}

Однако я не уверен, как на самом деле вывести значение из цикла.В настоящее время ошибка компилятора составляет temporary value dropped while borrowed (при &r.str.to_owned()), хотя я предпринял много других попыток, но безрезультатно.

Единственный способ, с помощью которого я нашел работу, - это создать какой-то видлокальной tmp_str переменной и сделайте хак, как это:

match current {
    Some(r) => {
        tmp_str.clone_from(&r.str);
        last = Some(&tmp_str);
    }
    None => {
        last = None;
    }
}

Но это не похоже на то, как это должно быть сделано.

Ответы [ 2 ]

0 голосов
/ 02 февраля 2019

В вашем коде остается неясным, кем должен быть владелец String, на который есть ссылка в last: Option<&str>.Вы можете ввести дополнительную изменяемую локальную переменную, которой принадлежит строка.Но тогда у вас будет две переменные: владелец и ссылка, которые кажутся избыточными.Было бы намного проще сделать last владельцем:

struct MyRes {
    str: String,
}

fn main() {
    let times = 10;
    let mut last: Option<String> = None;

    for _i in 0..times {
        last = do_something(&last).map(|r| r.str);
    }
}

fn do_something(_o: &Option<String>) -> Option<MyRes> {
    Some(MyRes {
        str: "whatever string".to_string(),
    })
}

В do_something вы можете просто передать весь аргумент по ссылке, это, скорее всего, будет тем, что вы хотели.Также обратите внимание, что называть собственную структуру Result - плохая идея, потому что Result - это такая распространенная черта, встроенная глубоко в компилятор (? -оператор и т. Д.).


Дополнительный вопрос: Option<&str> или Option<String>?

Оба Option<&str> и Option<String> имеют различные компромиссы.Один лучше для передачи строковых литералов, другой лучше для передачи принадлежащих String s.Я бы на самом деле предложил использовать ни один, и вместо этого сделать функцию универсальной по типу S, который реализует AsRef<str>.Вот сравнение различных методов:

fn do_something(o: &Option<String>) {
    let _a: Option<&str> = o.as_ref().map(|r| &**r);
    let _b: Option<String> = o.clone();
}
fn do_something2(o: &Option<&str>) {
    let _a: Option<&str> = o.clone(); // do you need it?
    let _b: Option<String> = o.map(|r| r.to_string());
}
fn do_something3<S: AsRef<str>>(o: &Option<S>) {
    let _a: Option<&str> = o.as_ref().map(|s| s.as_ref());
    let _b: Option<String> = o.as_ref().map(|r| r.as_ref().to_string());
}

fn main() {
    let x: Option<String> = None;
    let y: Option<&str> = None;

    do_something(&x);                           // nice
    do_something(&y.map(|r| r.to_string()));    // awkward & expensive

    do_something2(&x.as_ref().map(|x| &**x));   // cheap but awkward
    do_something2(&y);                          // nice

    do_something3(&x);                          // nice
    do_something3(&y);                          // nice, in both cases
}

Обратите внимание, что не все из вышеперечисленных комбинаций очень идиоматичны, некоторые добавляются только для полноты (например, запрос AsRef<str>, а затем создание собственного Stringкажется немного странным).

0 голосов
/ 02 февраля 2019

r.str.to_owned() является временным значением. Вы можете взять ссылку на временную , но поскольку временное значение обычно удаляется (уничтожается) в конце самого внутреннего вмещающего оператора, ссылка в этом месте становится висячей.В этом случае «внутреннее вложенное выражение» является либо последней строкой цикла, либо самим телом цикла - я не уверен точно, какой из них применяется здесь, но это не имеет значения, потому что в любом случае выпопытка заставить last содержать ссылку на String, который вскоре будет удален, что делает last непригодным для использования.Компилятор вправе помешать вам использовать его снова на следующей итерации цикла.

Самое простое решение - просто не делать last ссылкой вообще - в примере это не нужно илижелательно.Просто используйте Option<String>:

fn main() {
    let times = 10;
    let mut last = None;

    for _ in 0..times {
        last = match do_something(last) {
            Some(r) => Some(r.str),
            None => None,
        };
    }
}

fn do_something(_: Option<String>) -> Option<Result> {
    // ...
}

Существуют также способы заставить справочную версию работать;вот один из них:

let mut current;  // lift this declaration out of the loop so `current` will have
                  // a lifetime longer than one iteration
for _ in 0..times {
    current = do_something(last);
    last = match current {
        Some(ref r) => Some(&r.str),  // borrow from `current` in the loop instead
                                      // of from a newly created String
        None => None,
    };
}

Возможно, вы захотите сделать это, если ваш код сложнее, чем пример, и использование String будет означать много потенциально дорогих .clone() с.

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