Время жизни Option:: аргумент карты

Я помещаю строку в Option и попробуйте отобразить его, например, чтобы обрезать строку:

fn main() {
    let s = "     i want to be trimmed    ".to_string();
    let s_opt = Some(s);

    let x = s_opt.map(|z| z.trim());
    //     let x = s_opt.map(|z| z.trim().to_string());

    println!("'{:?}'", x);
}

Компилятор показывает ошибку при жизни

error[E0597]: `z` does not live long enough
 --> src/main.rs:5:27
  |
5 |     let x = s_opt.map(|z| z.trim());
  |                           ^      - `z` dropped here while still borrowed
  |                           |
  |                           borrowed value does not live long enough
...
9 | }
  | - borrowed value needs to live until here

Это понятно, так как z определяется только в замыкании, а аргумент передается по значению.

Поскольку исходная переменная s живет для всего блока, не должен ли компилятор понять, что z на самом деле s?

Единственный способ заставить это работать, добавив to_string (см. закомментированную строку), но затем я создаю новый строковый объект.

Другое решение, которое я нашел, это сделать s_opt тип Option<&String> (см. второй блок кода), но так как функции не могут возвращать этот тип типа, это на самом деле не вариант.

fn main() {
    let s = "     i want to be trimmed    ".to_string();
    let s_opt = Some(&s);

    let x = s_opt.map(|z| z.trim());
    println!("'{:?}'", x);
}

Есть ли что-то, что я упустил или не будет лучше, если реализация по умолчанию map будет похоже на это?

fn my_map<'r, F>(o: &'r Option<String>, f: F) -> Option<&'r str>
where
    F: Fn(&'r String) -> &'r str,
{
    match *o {
        None => None,
        Some(ref x) => Some(f(x)),
    }
}

fn main() {
    let s = "     i want to be trimmed    ".to_string();
    let s_opt = Some(s);

    let x = my_map(&s_opt, |x| x.trim());
    println!("'{:?}'", x);
}

2 ответа

Решение

map функция потребляет повторяющиеся значения, поэтому они больше не существуют после вызова данного замыкания. Вы не можете вернуть ссылки на них.

Лучшее решение было бы на месте обрезки на String непосредственно. К сожалению, в стандартной библиотеке нет ни одной.

Ваше второе решение также возможно с небольшим изменением. Вместо &String вы берете &str:

fn main() {
    let s = "text".to_string();
    let s_opt = Some(s.as_str());
    let x = s_opt.map(|z| z.trim());
    println!("{:?}", x);
}

Как указано, Option::map потребляет исходное значение для получения выходного значения. Это наиболее гибкая и эффективная реализация, поскольку вы можете преобразовать Option<A> для Option<B> без необходимости клонировать исходное значение.

Решением в этом случае является преобразование вашего Option<String> (действительно &Option<String>) в Option<&String> с помощью Option::as_ref, Когда у вас есть Option<&String>, you can consume it without losing ownership of the original Option`:

fn main() {
    let s = Some("     i want to be trimmed    ".to_string());

    let x = s.as_ref().map(|z| z.trim());

    println!("{:?}", x);
}
Другие вопросы по тегам