rust - 使用带有 iter().map() 的函数 - 作为命名函数与作为闭包
问题描述
这两个片段来自 Python,在行为上几乎完全等效。它们都工作并提供相同的输出,尽管它们产生的字节码略有不同。
def lower_case(s):
return s.lower()
map(lower_case, ["A", "B"])
对比
def lower_case(s):
return s.lower()
map(lambda s: lower_case(s), ["A", "B"])
学习 Rust,我正试图围绕以下案例展开思考。拥有一个接收字符串并返回第一个字符大写的字符串版本的函数:
pub fn capitalize_first(input: &str) -> String {
let mut c = input.chars();
match c.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + c.as_str(),
}
}
用另一个接受字符串向量的函数包装这个函数是有趣的地方:
pub fn capitalize_first(input: &str) -> String {
let mut c = input.chars();
match c.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + c.as_str(),
}
}
pub fn capitalize_words(words: Vec<&str>) -> Vec<String> {
words.iter().map(|w| capitalize_first(w)).collect::<Vec<String>>()
}
这有效,但更换
words.iter().map(|w| capitalize_first(w)).collect::<Vec<String>>()
和
words.iter().map(capitalize_first).collect::<Vec<String>>()
导致编译失败并出现以下错误:
error[E0631]: type mismatch in function arguments
--> exercises/standard_library_types/iterators2.rs:27:22
|
12 | pub fn capitalize_first(input: &str) -> String {
| ---------------------------------------------- found signature of `for<'r> fn(&'r str) -> _`
...
27 | words.iter().map(capitalize_first).collect::<Vec<String>>()
| ^^^^^^^^^^^^^^^^ expected signature of `fn(&&str) -> _`
error[E0599]: no method named `collect` found for struct `std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>` in the current scope
--> exercises/standard_library_types/iterators2.rs:27:40
|
27 | words.iter().map(capitalize_first).collect::<Vec<String>>()
| ^^^^^^^ method not found in `std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>`
|
::: C:\Users\Adi\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\src\libcore\iter\adapters\mod.rs:809:1
|
809 | pub struct Map<I, F> {
| -------------------- doesn't satisfy `_: std::iter::Iterator`
|
= note: the method `collect` exists but the following trait bounds were not satisfied:
`<for<'r> fn(&'r str) -> std::string::String {capitalize_first} as std::ops::FnOnce<(&&str,)>>::Output = _`
which is required by `std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>: std::iter::Iterator`
`for<'r> fn(&'r str) -> std::string::String {capitalize_first}: std::ops::FnMut<(&&str,)>`
which is required by `std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>: std::iter::Iterator`
`std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>: std::iter::Iterator`
which is required by `&mut std::iter::Map<std::slice::Iter<'_, &str>, for<'r> fn(&'r str) -> std::string::String {capitalize_first}>: std::iter::Iterator`
我相信我明白。
但是,按照建议进行更改
capitalize_first(input: &str)
到
capitalize_first(input: &&str)
通过编译,但现在测试失败(显然,因为用,而不是capitalize_first
调用):&str
&&str
error[E0308]: mismatched types
--> exercises/standard_library_types/iterators2.rs:40:37
|
40 | assert_eq!(capitalize_first("hello"), "Hello");
| ^^^^^^^ expected `&str`, found `str`
|
= note: expected reference `&&str`
found reference `&'static str`
error[E0308]: mismatched types
--> exercises/standard_library_types/iterators2.rs:45:37
|
45 | assert_eq!(capitalize_first(""), "");
| ^^ expected `&str`, found `str`
|
= note: expected reference `&&str`
found reference `&'static str`
是否有任何妥协可以允许words.iter().map(capitalize_first).collect::<Vec<String>>()
工作,同时仍然允许现有的测试capitalize_first
通过?
map(capitalize_first)
和之间的区别map(|x| capitalize_first(x))
可能是可以忽略的(在视觉上、语法上和性能上),但是定义一个接受参数的闭包只是为了调用具有相同参数的函数是非常烦人的(有些人甚至会说这是一种反模式)。
解决方案
您可以更改capitalize_first
为使用具有AsRef
特征的泛型:
pub fn capitalize_first<T: AsRef<str>>(input: T) -> String {
// use .as_ref() here to convert to &str
let mut c = input.as_ref().chars();
match c.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + c.as_str(),
}
}
这将使它与&str
和&&str
(String
以及任何数量的嵌套引用str
)兼容。
推荐阅读
- python - MusicBrainz Picard 错误标记视频文件
- haskell - 从输入将 [Int] 转换为 [Double]
- hyperledger-fabric - Hyperledger Fabric 上的多个通道 - IBM Bluemix 入门计划
- git - 使用 bundle 增量同步 git repos
- node.js - 无法在 Windows 上安装 Ionic
- nginx - 使用 Nginx 在 Ubuntu 服务器上部署多个 .Net Core 站点
- cloudkit - 获取 CloudKit 用户的(真实)电子邮件地址
- angular - 如何将 UI 与微服务捆绑在一起
- python - 如何为 JSON 字符串对象提供要读取的属性?
- python - 使用 Python 计算每月复利