Я не могу понять, почему следующий код выдает ошибку this generic parameter must be used with a generic type parameter
use anyhow::Result;
use std::path::Path;
fn test_file(path: impl AsRef<Path>, filename: &str) -> Result<Option<impl AsRef<Path>>> {
...
if path.join(filename).exist() {
Ok(Some(path))
} else if current_directory == path {
Ok(None)
} else {
test_file(path.parent().context("{filename} not found")?, filename)
}
}
Как вы можете видеть, это рекурсивная функция, и я получаю ошибку в условии else при передаче пути к родительскому каталогу, но, что удивительно, если я изменю тип возвращаемого значения на Result<bool>
со следующим телом функции, тогда компилятор пройдет без каких-либо ошибок.
use anyhow::Result;
use std::path::Path;
fn test_file(path: impl AsRef<Path>, filename: &str) -> Result<bool> {
...
if path.join(filename).exist() {
Ok(true)
} else if current_directory == path {
Ok(false)
} else {
test_file(path.parent().context("{filename} not found")?, filename)
}
}
Я не могу понять, что я здесь делаю не так.
Верно, но я не могу понять, почему компилятор жалуется на impl AsRef<Path>
?
Если честно, я тоже не могу этого понять. Если вы последуете предложению компилятора и выполните test_file<T: AsRef<Path>>(path: T,
, он все равно будет жаловаться (с совершенно бессмысленной диагностикой, я мог бы добавить), но если вы это сделаете test_file(path: &Path,
, то он будет работать. Кажется, это комбинация path
общего и возвращаемого положения impl
, но будь я проклят, если смогу понять, почему. Мне он чем-то напоминает запах жука, но я не могу сказать этого наверняка.
@cdhowie, хотя ошибка кажется запутанной, она буквально описывает проблему, вы не можете использовать test_file
с &Path
, потому что это не гарантирует возврат того же типа, что и при вызове с T
, вы должны использовать параметр типа T
→ "этот общий параметр должен использоваться с параметром универсального типа» — это буквально то, что вам нужно сделать.
ссылка на репродукцию игровой площадки: play.rust-lang.org/…
@cafce25 cafce25 Тогда формулировка сообщения об ошибке просто ужасна.
Простой ответ — всегда возвращаться PathBuf
. Сообщить вызывающему объекту о том, что у него есть собственное значение, на самом деле полезно, поскольку это позволяет избежать ненужного клонирования, если ему понадобится превратить его в собственное значение.
fn test_file(path: impl AsRef<Path>, filename: &str) -> Result<Option<PathBuf>> {
Проблема в том, что возвращаемый тип может зависеть от параметра типа1, поэтому единственный допустимый test_file
для рекурсии должен иметь те же параметры типа, что и текущий, но &Path
(результат path.parent().context("{filename} not found")?
) не обязательно совпадает с тип передан.
Вот пример этого:
trait Foo {
type A: AsRef<str>;
fn foo(&self) -> Self::A;
}
impl Foo for () {
type A = &'static str;
fn foo(&self) -> &'static str {
"unit"
}
}
impl Foo for String {
type A = String;
fn foo(&self) -> String {
String::from("string")
}
}
fn test(x: impl Foo) -> impl AsRef<str> {
x.foo()
}
fn main() {
dbg!(test(String::new()).as_ref());
dbg!(test(()).as_ref());
}
1) analyzing the body would show it's always a wrapped PathBuf
but that's not something the compiler does factor in
Почему ты возвращаешься
impl AsRef<Path>
? Просто вернитесьPathBuf
.