以下代码可以正常工作(cargo +nightly run
):
fn main() {
let res: Result<(), String> = Err(String::from("hi"));
println!("{}", res.map_err(shout).unwrap_err())
}
fn shout(s: String) -> String {
s.to_ascii_uppercase()
}
Clippy (cargo +nightly Clippy
) 发出一个(合理的)警告:
warning: this argument is passed by value, but not consumed in the function body
--> src/main.rs:6:13
|
6 | fn shout(s: String) -> String {
| ^^^^^^ help: consider changing the type to: `&str`
将代码更改为建议的版本
fn shout(s: &str) -> String {
s.to_ascii_uppercase()
}
导致编译器错误:
error[E0631]: type mismatch in function arguments
--> src/main.rs:3:24
|
3 | println!("{}", res.map_err(shout).unwrap_err())
| ^^^^^^^ expected signature of `fn(std::string::String) -> _`
...
6 | fn shout(s: &str) -> String {
| --------------------------- found signature of `for<'r> fn(&'r str) -> _`
正确的 react 方式是什么?当然,我可以简单地执行 #![cfg_attr(feature="clippy",allow(needless_pass_by_value))]
但这对我来说感觉不对。有没有办法将 map_err
与 shout
版本一起使用进行引用?
最佳答案
你能做的最好的事情就是使用完全关闭:
res.map_err(|x| shout(&x)).unwrap_err()
您的原始表单需要两个步骤才能生效:
- 它需要将参数传递给闭包并将其转换为引用。
- 它需要将
&String
转换为&str
。
此外,当值在范围内时,它需要执行这两项操作,这样它就不会以悬空引用结束。这些都不是“短”形式的闭包现在可以处理的事情——类型必须完全匹配。
如果您确实想要避免关闭,您可以针对此特定情况:
res.as_ref().map_err(String::as_str).map_err(shout).unwrap_err()
// ^~~~~~ ^~~~~~~~~~~~~~
// | |
// | |- Convert `&String` to `&str`
// |
// |- Get a reference (`&String`)
我其实argued for the ability for your original code to work作为人体工程学计划的一部分,但它似乎并没有获得关注。
像编程中的许多问题一样,您可以通过添加更多抽象来“解决”这个问题。在这里,我们引入一个特质来体现“可以喊出来的错误”的概念:
fn main() {
let e1 = Err::<(), _>(String::from("hi"));
println!("{}", e1.map_err(ShoutyError::shout).unwrap_err());
let e2 = Err::<(), _>(42);
println!("{}", e2.map_err(ShoutyError::shout).unwrap_err());
}
trait ShoutyError {
fn shout(self) -> String;
}
impl ShoutyError for String {
fn shout(self) -> String {
self.to_ascii_uppercase()
}
}
impl ShoutyError for i32 {
fn shout(self) -> String {
format!("I YELL {}", self)
}
}
如果您觉得需要它,您还可以使用包装函数来保留确切的初始代码:
fn shout<E: ShoutyError>(e: E) -> String {
e.shout()
}
I'd like to have a function
adapt
that takes one functionf : &T -> U
and returns a new functiong : T -> U
.
这是可能的,但仅限于夜间 Rust:
#![feature(conservative_impl_trait)]
fn adapt<F, T, U>(f: F) -> impl Fn(T) -> U
where
F: Fn(&T) -> U,
{
move |arg| f(&arg)
}
不幸的是,它不能解决您的问题,因为 shout
不接受 &String
并且这需要 str
为 大小
类型。
更详细的解决方案涉及 AsRef
:
#![feature(conservative_impl_trait)]
fn adapt<F, T1, T2, U>(f: F) -> impl Fn(T1) -> U
where
F: Fn(&T2) -> U,
T1: AsRef<T2>,
T2: ?Sized,
{
move |arg| f(arg.as_ref())
}
关于reference - 当函数需要引用闭包参数时,如何将函数用作闭包?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48809407/