如何解决接受字符串迭代器的函数的正确签名
我对用于生成字符串切片的迭代器的正确类型感到困惑。
fn print_strings<'a>(seq: impl IntoIterator<Item = &'a str>) {
for s in seq {
println!("- {}",s);
}
}
fn main() {
let arr: [&str; 3] = ["a","b","c"];
let vec: Vec<&str> = vec!["a","c"];
let it: std::str::Split<'_,char> = "a b c".split(' ');
print_strings(&arr);
print_strings(&vec);
print_strings(it);
}
使用 <Item = &'a str>
,arr
和 vec
调用不会编译。相反,如果我使用 <Item = &'a'a str>
,它们可以工作,但 it
调用无法编译。
当然,我也可以使 Item 类型通用,并且这样做
fn print_strings<'a,I: std::fmt::display>(seq: impl IntoIterator<Item = I>)
但它变得愚蠢。肯定必须有一个规范的“字符串值迭代器”类型吗?
解决方法
您看到的错误是意料之中的,因为 seq
是 &Vec<&str>
并且 &Vec<T>
使用 IntoIterator
实现了 Item=&T
,因此使用您的代码,您最终会得到Item=&&str
在所有情况下您都期望它是 Item=&str
。
执行此操作的正确方法是扩展 Item
类型,以便它可以同时处理 &str
和 &&str
。您可以通过使用更多泛型来做到这一点,例如
fn print_strings(seq: impl IntoIterator<Item = impl AsRef<str>>) {
for s in seq {
let s = s.as_ref();
println!("- {}",s);
}
}
这要求 Item
是您可以从中检索 &str
的东西,然后在您的循环中 .as_ref()
将返回您正在寻找的 &str
。
这还有一个额外的好处,即您的代码也适用于 Vec<String>
和任何其他实现 AsRef<str>
的类型。
TL;DR 您使用的签名很好,是调用者为迭代器提供了错误的 Item
- 但可以轻松修复。
如另一个答案中所述,print_string()
不接受 &arr
和 &vec
,因为 IntoIterator
for &[T; n]
和 &Vec<T>
yield 引用 T
。这是因为 &Vec
本身是一个引用,不允许使用 Vec
以将 T
值移出它。它可以做的是分发对位于 T
内的 Vec
项的引用,即类型为 &T
的项。对于不编译的调用者,容器包含 &str
,因此它们的迭代器分发 &&str
。
除了使 print_string()
更通用之外,另一种解决问题的方法是一开始就正确调用它。例如,这些都编译:
print_strings(arr.iter().map(|sref| *sref));
print_strings(vec.iter().copied());
print_strings(it);
iter()
是切片提供的方法(因此可用于数组和 Vec
),它遍历对元素的引用,就像 IntoIterator
的 &Vec
一样。我们显式调用它是为了能够调用 map()
以显而易见的方式将 &&str
转换为 &str
- 通过使用 *
运算符取消引用 &&str
。 copied()
迭代器适配器是另一种表达方式,可能比 map(|x| *x)
更隐蔽。 (还有 cloned()
,相当于 map(|x| x.clone())
。)
如果您有一个包含 print_strings()
值的容器,也可以调用 String
:
let v = vec!["foo".to_owned(),"bar".to_owned()];
print_strings(v.iter().map(|s| s.as_str()));
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。