如何在 Rust 中作为函数参数接受 Vec<String> 和 Vec<str> 两种类型

9

我正在开发我的第一个 Rust 箱(crate),想要通过让API接受 foo(vec!["bar", "baz"])foo(vec![String::from("foo"), String::from("baz")]) 两种形式的参数来使其更加用户友好。

到目前为止,我已经成功地让 API 接受了 String&str 类型的参数,但对于 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);
    }
}

我得到的编译器错误是:
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`

2
我已经撤回了我的投票,另一个问题对于新手来说可能太过混乱,对此我感到抱歉。 - Stargateur
使用 AsRef<str> 可能比 Into<String> 更有用。此外,请注意,通过转换为 String,您可能会不必要地将字符串分配到堆中。 - SOFe
2个回答

10
你可以选择使用完全的泛型,不需要强制用户使用 Vec,最好是使用实现了 IntoIterator 的泛型类型,这样你只需要编写 Item 实现 Into<String>,语法有些奇怪但很合理。你需要第二个泛型类型来完成它。我将类型为迭代器的称为 I,类型为项的称为 T。

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);
    }
}

这可以解决你的问题,同时使你的函数更加通用。

我甚至还没有考虑允许其他可迭代结构,但这绝对是一个更灵活的解决方案!谢谢。 - Martin Sotirov

5

这个不起作用是因为你的迭代器没有给你S,而是给了你&S

如果你想要将字符串从向量中移出,那么你必须使它可变并排空:

fn foo_many<S: Into<String>>(mut strings: Vec<S>) -> Vec<String> {
    strings.drain(..).map(|s| s.into()).collect()
}

游乐场


@MartinSotirov,我添加了一个游乐场的链接。我有遗漏什么吗? - Denys Séguret
抱歉,我错过了链接。那正是我所需要的。谢谢。 - Martin Sotirov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接