I'm working on my first Rust crate and I wanted to make my API a bit more user friendly by allowing both foo(vec!["bar", "baz"])
and foo(vec![String::from("foo"), String::from("baz")])
.
So far I've managed to accept both String
and &str
but I'm struggling to do the same for Vec<T>
.
fn foo<S: Into<String>>(string: S) -> String {
string.into()
}
fn foo_many<S: Into<String>>(strings: Vec<S>) -> Vec<String> {
strings.iter().map(|s| s.into()).collect()
}
fn main() {
println!("{}", foo(String::from("bar")));
println!("{}", foo("baz"));
for string in foo_many(vec!["foo", "bar"]) {
println!("{}", string);
}
}
The compiler error I get is:
error[E0277]: the trait bound `std::string::String: std::convert::From<&S>` is not satisfied
--> src/main.rs:6:30
|
6 | strings.iter().map(|s| s.into()).collect()
| ^^^^ the trait `std::convert::From<&S>` is not implemented for `std::string::String`
|
= help: consider adding a `where std::string::String: std::convert::From<&S>` bound
= note: required because of the requirements on the impl of `std::convert::Into<std::string::String>` for `&S`
You can go for full generic, you don't need to force user to use a Vec
, better you can take a generic type that implement IntoIterator
that you just have to write that Item
implement Into<String>
, the syntax is somehow strange and logic. You need a second generic type to do it. I call I
the type that will be the iterator and T the Item type.
fn foo<S: Into<String>>(string: S) -> String {
string.into()
}
fn foo_many<I, T>(iter: I) -> Vec<String>
where
I: IntoIterator<Item = T>,
T: Into<String>,
{
iter.into_iter().map(Into::into).collect()
}
fn main() {
println!("{}", foo(String::from("bar")));
println!("{}", foo("baz"));
for string in foo_many(vec!["foo", "bar"]) {
println!("{}", string);
}
for string in foo_many(vec![foo("foo"), foo("baz")]) {
println!("{}", string);
}
}
This solve your problem and make your function even more generic.
This doesn't work because your iteration doesn't give you S
but &S
.
If you want to move the strings out of the vector, you must make it mutable and drain it:
fn foo_many<S: Into<String>>(mut strings: Vec<S>) -> Vec<String> {
strings.drain(..).map(|s| s.into()).collect()
}
playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With