我在编写一个将字符串集合作为参数的函数时遇到了麻烦。我的功能如下:
type StrList<'a> = Vec<&'a str>;
fn my_func(list: &StrList) {
for s in list {
println!("{}", s);
}
}
如果我按预期将Vec<&'a str>
传递给函数,一切顺利。但是,如果我传递Vec<String>
编译器抱怨:
error[E0308]: mismatched types
--> src/main.rs:13:13
|
13 | my_func(&v2);
| ^^^ expected &str, found struct `std::string::String`
|
= note: expected type `&std::vec::Vec<&str>`
= note: found type `&std::vec::Vec<std::string::String>`
这是主要使用的:
fn main() {
let v1 = vec!["a", "b"];
let v2 = vec!["a".to_owned(), "b".to_owned()];
my_func(&v1);
my_func(&v2);
}
我的函数无法获取拥有字符串的向量。相反,如果我将StrList
类型更改为:
type StrList = Vec<String>;
第一次通话失败,第二次通话失败。
一种可能的解决方案是以这种方式从Vec<&'a str>
生成v2
:
let v2_1 : Vec<_> = v2.iter().map(|s| s.as_ref()).collect();
但对我来说这似乎很奇怪。 my_func
不应该关心字符串的所有权。
我应该使用什么样的签名来my_func
支持自有字符串和字符串引用的向量?
答案 0 :(得分:18)
尽管String
和&str
密切相关,但它们并不完全相同。这是你的向量在内存中的样子:
v1 ---> [ { 0x7890, // pointer to "a" + 7 unused bytes
1 } // length of "a"
{ 0x7898, // pointer to "b" + 7 unused bytes
1 } ] // length
v2 ---> [ { 0x1230 // pointer to "a" + 7 unused bytes (a different copy)
8 // capacity
1 } // length
{ 0x1238 // pointer ...
8 // capacity
1 } ] // length
这里每行的内存量相同(四个或八个字节,具体取决于指针大小)。你不能把它们中的一个记忆下来并像对方一样对待它。内存布局不匹配。这些物品的大小不同,布局也不同。例如,如果v1
存储从地址X
开始的项目,而v2
存储从地址Y
开始的项目,则v1[1]
位于地址{{1}但是X + 8
位于地址v2[1]
。
你可以做的是写一个通用函数:
Y + 12
然后编译器可以为fn my_func<T: AsRef<str>>(list: &[T]) {
for s in list {
println!("{}", s.as_ref());
}
}
和&[String]
以及其他类型生成适当的代码(如果它们实现&[&str]
。
答案 1 :(得分:3)
要在delnan's great answer上构建,我想指出您可以在此处添加的更多级别的泛型。你说:
字符串集合
但是有更多类型的集合而不是切片和向量!在您的示例中,您关心的是仅向前,一次一次访问项目。这是Iterator
的完美示例。下面,我已经将您的函数更改为接受任何可以转换为迭代器的类型。然后,您可以传递更多类型的东西。我使用了HashSet
作为示例,但请注意,您也可以传入v1
和v2
而不是&v1
或&v2
,并使用它们。
use std::collections::HashSet;
fn my_func<I>(list: I)
where I: IntoIterator,
I::Item: AsRef<str>,
{
for s in list {
println!("{}", s.as_ref());
}
}
fn main() {
let v1 = vec!["a", "b"];
let v2 = vec!["a".to_owned(), "b".to_owned()];
let v3 = {
let mut set = HashSet::new();
set.insert("a");
set.insert("b");
set.insert("a");
set
};
let v4 = {
let mut set = HashSet::new();
set.insert("a".to_owned());
set.insert("b".to_owned());
set.insert("a".to_owned());
set
};
my_func(&v1);
my_func(v1);
my_func(&v2);
my_func(v2);
my_func(&v3);
my_func(v3);
my_func(&v4);
my_func(v4);
}