Rust 类型不匹配但前提是我不使用类型注释
Rust type mismatch but only if I don't use a type annotation
动机
我想读取光盘上多个文件的值流。这些可能是 CSV 文件,或者制表符分隔的文件,或者一些专有的二进制格式。因此,我希望处理读取多个文件的函数将 Path -> Iterator<Data>
函数作为参数。如果我理解正确,在 Rust 中我需要将迭代器和函数本身装箱,因为它们没有大小。因此我的阅读功能应该是(我只是在这里使用 i32
作为我的数据的简单代理):
fn foo(read_from_file: Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>) {
panic!("Not implemented");
}
为了测试,我不想从光盘中读取实际文件。我希望我的测试数据就在测试模块中。这大致是我想要的,但为了简单起见,我只是将它放入 bin 项目的主体中:
use std::path::Path;
fn foo(read_from_file: Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>) {
panic!("Not implemented");
}
fn main() {
let read_from_file = Box::new(|path: &Path| Box::new(match path.as_os_str().to_str().unwrap() {
"/my_files/data.csv" => vec![1, 2, 3],
"/my_files/data_2.csv" => vec![4, 5, 6],
_ => panic!("Invalid filename"),
}.into_iter()));
foo(read_from_file);
}
错误
这给了我一个编译错误:
Compiling iter v0.1.0 (/home/harry/coding/rust_sandbox/iter)
error[E0271]: type mismatch resolving `for<'r> <[closure@src/main.rs:9:35: 13:19] as FnOnce<(&'r Path,)>>::Output == Box<(dyn Iterator<Item = i32> + 'static)>`
--> src/main.rs:15:9
|
15 | foo(read_from_file);
| ^^^^^^^^^^^^^^ expected trait object `dyn Iterator`, found struct `std::vec::IntoIter`
|
= note: expected struct `Box<(dyn Iterator<Item = i32> + 'static)>`
found struct `Box<std::vec::IntoIter<{integer}>>`
= note: required for the cast to the object type `dyn for<'r> Fn(&'r Path) -> Box<(dyn Iterator<Item = i32> + 'static)>`
For more information about this error, try `rustc --explain E0271`.
error: could not compile `iter` due to previous error
我不太明白。 std::vec::IntoIter
没有实现 Iterator
,在这种情况下我不明白为什么这是一个输入错误?
修复,我也不明白
如果我添加显式类型注释 Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>
,则编译:
use std::path::Path;
fn foo(read_from_file: Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>) {
panic!("Not implemented");
}
fn main() {
let read_from_file : Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>
= Box::new(|path: &Path| Box::new(match path.as_os_str().to_str().unwrap() {
"/my_files/data.csv" => vec![1, 2, 3],
"/my_files/data_2.csv" => vec![4, 5, 6],
_ => panic!("Invalid filename"),
}.into_iter()));
foo(read_from_file);
我很困惑为什么会这样。我对 Rust 的理解是,在 let
定义中,显式类型是可选的——除非编译器无法推断它,在这种情况下,编译器应该发出 error[E0283]: type annotations required
.
指向 Box<dyn Iterator<Item=i32>>
等动态大小类型 (DST) 的指针是 。 Box<std::vec::IntoIter<i32>>
不是指向 DST 的指针(因为 IntoIter
的大小已知),因此可以是一个简单地指向堆上 IntoIter
实例的“瘦”指针.
胖指针的创建和使用比瘦指针更昂贵。这就是为什么,正如 @Aplet123 提到的,您需要以某种方式 显式告诉编译器 (通过类型注释或 as
强制转换)您想要强制转换瘦 Box<std::vec::IntoIter<i32>>
由你的闭包生成的指向胖 Box<dyn Iterator<Item=i32>>
指针的指针。
请注意,如果您删除 let
绑定并在 foo
函数调用的参数列表中创建闭包,则编译器会使闭包必须 return 一个胖指针因为 foo
.
期望的参数类型
对我来说,这看起来像是类型推断的失败,因为闭包无法推断它需要 return 指向 v-table 的指针(来自 dyn Iterator
).
不过,我建议 Box<dyn Foo>
在这里可能没有必要。确实,由于 Iterator
是一个特征,您无法在编译时知道它的大小,从某种意义上说,您可以。
Rust 将泛型代码“单态化”,这意味着它会为每个与之一起使用的具体类型生成泛型 functions/structs/etc 的副本。例如,如果您有:
struct Foo<T> {
value: T
}
fn main() {
let _ = Foo { value: "hello" };
let _ = Foo { value: 123 };
}
它将生成一个 Foo_str_'static
和一个 Foo_i32
(大致来说)并根据需要替换它们。
您可以利用此漏洞在使用特征时通过泛型使用静态分派。您的函数可以重写为:
fn foo<F, I>(read_from_file: F)
where
F: Fn(&Path) -> I,
I: Iterator<Item = i32>,
{
unimplemented!()
}
fn main() {
// note the lack of boxing
let read_from_file = |path: &Path| {
// ...
};
foo(read_from_file);
}
此代码(很可能但我还没有进行基准测试)更快、更惯用,并且使编译器错误消失。
动机
我想读取光盘上多个文件的值流。这些可能是 CSV 文件,或者制表符分隔的文件,或者一些专有的二进制格式。因此,我希望处理读取多个文件的函数将 Path -> Iterator<Data>
函数作为参数。如果我理解正确,在 Rust 中我需要将迭代器和函数本身装箱,因为它们没有大小。因此我的阅读功能应该是(我只是在这里使用 i32
作为我的数据的简单代理):
fn foo(read_from_file: Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>) {
panic!("Not implemented");
}
为了测试,我不想从光盘中读取实际文件。我希望我的测试数据就在测试模块中。这大致是我想要的,但为了简单起见,我只是将它放入 bin 项目的主体中:
use std::path::Path;
fn foo(read_from_file: Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>) {
panic!("Not implemented");
}
fn main() {
let read_from_file = Box::new(|path: &Path| Box::new(match path.as_os_str().to_str().unwrap() {
"/my_files/data.csv" => vec![1, 2, 3],
"/my_files/data_2.csv" => vec![4, 5, 6],
_ => panic!("Invalid filename"),
}.into_iter()));
foo(read_from_file);
}
错误
这给了我一个编译错误:
Compiling iter v0.1.0 (/home/harry/coding/rust_sandbox/iter)
error[E0271]: type mismatch resolving `for<'r> <[closure@src/main.rs:9:35: 13:19] as FnOnce<(&'r Path,)>>::Output == Box<(dyn Iterator<Item = i32> + 'static)>`
--> src/main.rs:15:9
|
15 | foo(read_from_file);
| ^^^^^^^^^^^^^^ expected trait object `dyn Iterator`, found struct `std::vec::IntoIter`
|
= note: expected struct `Box<(dyn Iterator<Item = i32> + 'static)>`
found struct `Box<std::vec::IntoIter<{integer}>>`
= note: required for the cast to the object type `dyn for<'r> Fn(&'r Path) -> Box<(dyn Iterator<Item = i32> + 'static)>`
For more information about this error, try `rustc --explain E0271`.
error: could not compile `iter` due to previous error
我不太明白。 std::vec::IntoIter
没有实现 Iterator
,在这种情况下我不明白为什么这是一个输入错误?
修复,我也不明白
如果我添加显式类型注释 Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>
,则编译:
use std::path::Path;
fn foo(read_from_file: Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>) {
panic!("Not implemented");
}
fn main() {
let read_from_file : Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>
= Box::new(|path: &Path| Box::new(match path.as_os_str().to_str().unwrap() {
"/my_files/data.csv" => vec![1, 2, 3],
"/my_files/data_2.csv" => vec![4, 5, 6],
_ => panic!("Invalid filename"),
}.into_iter()));
foo(read_from_file);
我很困惑为什么会这样。我对 Rust 的理解是,在 let
定义中,显式类型是可选的——除非编译器无法推断它,在这种情况下,编译器应该发出 error[E0283]: type annotations required
.
指向 Box<dyn Iterator<Item=i32>>
等动态大小类型 (DST) 的指针是 Box<std::vec::IntoIter<i32>>
不是指向 DST 的指针(因为 IntoIter
的大小已知),因此可以是一个简单地指向堆上 IntoIter
实例的“瘦”指针.
胖指针的创建和使用比瘦指针更昂贵。这就是为什么,正如 @Aplet123 提到的,您需要以某种方式 显式告诉编译器 (通过类型注释或 as
强制转换)您想要强制转换瘦 Box<std::vec::IntoIter<i32>>
由你的闭包生成的指向胖 Box<dyn Iterator<Item=i32>>
指针的指针。
请注意,如果您删除 let
绑定并在 foo
函数调用的参数列表中创建闭包,则编译器会使闭包必须 return 一个胖指针因为 foo
.
对我来说,这看起来像是类型推断的失败,因为闭包无法推断它需要 return 指向 v-table 的指针(来自 dyn Iterator
).
不过,我建议 Box<dyn Foo>
在这里可能没有必要。确实,由于 Iterator
是一个特征,您无法在编译时知道它的大小,从某种意义上说,您可以。
Rust 将泛型代码“单态化”,这意味着它会为每个与之一起使用的具体类型生成泛型 functions/structs/etc 的副本。例如,如果您有:
struct Foo<T> {
value: T
}
fn main() {
let _ = Foo { value: "hello" };
let _ = Foo { value: 123 };
}
它将生成一个 Foo_str_'static
和一个 Foo_i32
(大致来说)并根据需要替换它们。
您可以利用此漏洞在使用特征时通过泛型使用静态分派。您的函数可以重写为:
fn foo<F, I>(read_from_file: F)
where
F: Fn(&Path) -> I,
I: Iterator<Item = i32>,
{
unimplemented!()
}
fn main() {
// note the lack of boxing
let read_from_file = |path: &Path| {
// ...
};
foo(read_from_file);
}
此代码(很可能但我还没有进行基准测试)更快、更惯用,并且使编译器错误消失。