Как использовать аргументы `AsRef`? - PullRequest
2 голосов
/ 27 сентября 2019

Я с трудом пытаюсь заставить AsRef работать чисто.

const DEFAULT: &str = "lib";

use std::path::{Path, PathBuf};

fn extend(p: &Path, q: Option<&Path>) -> PathBuf {
    let q: &Path = q.unwrap_or(DEFAULT.as_ref());
    p.join(q)
}

// DOES NOT COMPILE
fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
    let q: &Path = q.map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
    p.as_ref().join(q)
}

// DOES NOT COMPILE
fn extend2<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
    let q: &Path = match q {
        Some(x) => x.as_ref(),
        None => DEFAULT.as_ref(),
    };
    p.as_ref().join(q)
}

fn extend3<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
    match q {
        Some(x) => p.as_ref().join(x.as_ref()),
        None => p.as_ref().join(AsRef::<Path>::as_ref(DEFAULT)),
    }
}

Функция extend работает с Path ссылками, но я хочу обобщить этопринять аргументы типа AsRef<Path>, чтобы разрешить, например, строку.

Мои первые попытки extend1 и extend2 не проходят проверку заимствования, которая жалуется на время жизни x в обоих случаях.

Моя третья попытка, extend3, работает, но имеет очевидный недостаток дублирования кода, который становится все более серьезным с ростом тела функции.

Что лучшерешение в этих обстоятельствах?

1 Ответ

1 голос
/ 27 сентября 2019

Option::map потребляет внутреннее значение (если оно есть).Таким образом, в коде q.map(|x| x.as_ref()) замыкание принимает значение x.Вы можете проверить это, заметив, что q.map(|x: &Q| x.as_ref()) выдает ошибку типа, а q.map(|x: Q| x.as_ref()) - нет (все равно выдает ошибку времени жизни).Это означает, что когда вы вызываете x.as_ref(), создается новая ссылка на x, не связанная с какой-либо внешней ссылкой.Это означает, что ссылка действительна только внутри замыкания, но вы хотите использовать ее в оставшейся части extend1.

. Вместо этого вы должны заимствовать q, действительный доконец extend1.Этот заем q можно превратить в заимствование его содержимого (если оно есть), используя Option::as_ref() для преобразования &Option<Q> в Option<&Q> (простое использование q.as_ref() создаст заем q, который вам нужен).Тогда, когда вы используете карту, закрытие займет &Q, а не Q.Срок действия ссылки будет таким же, как и внешний заем q, поэтому он будет действовать до конца extend1 (при необходимости).

const DEFAULT: &str = "lib";

use std::path::{Path, PathBuf};

fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Option<Q>) -> PathBuf {
    let q: &Path = q.as_ref().map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
    p.as_ref().join(q)
}

(детская площадка)

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

const DEFAULT: &str = "lib";

use std::path::{Path, PathBuf};

fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: &P, q: &Option<Q>) -> PathBuf {
    let q: &Path = q.as_ref().map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
    p.as_ref().join(q)
}

(площадка)

или

const DEFAULT: &str = "lib";

use std::path::{Path, PathBuf};

fn extend1<P: AsRef<Path>, Q: AsRef<Path>>(p: &P, q: Option<&Q>) -> PathBuf {
    let q: &Path = q.map(|x| x.as_ref()).unwrap_or(DEFAULT.as_ref());
    p.as_ref().join(q)
}

(детская площадка)

Обратите внимание, что эта последняя версия не должна вызывать q.as_ref(), так как q уже имеет тип Option<&Q>.Эта версия наиболее точно соответствует вашей исходной функции extend.

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