Tob*_*ann 2 closures reference rust
以下代码工作正常 ( 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) 吐出一个(合理的)警告:
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()
}
将代码更改为建议的版本
fn shout(s: &str) -> String {
    s.to_ascii_uppercase()
}
导致编译器错误:
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`
什么是正确的反应方式?当然,我可以简单地做,#![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`)   
我实际上认为您的原始代码能够作为人体工程学计划的一部分工作,但它似乎并没有获得吸引力。
与编程中的许多问题一样,您可以通过添加更多抽象来“解决”这个问题。在这里,我们引入一个 trait 来体现“一个可以喊出的错误”的概念:
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()
}
我想要一个函数
adapt,它接受一个函数f : &T -> U并返回一个新函数g : 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它不接受 a&String而这需要str是一种Sized类型。
更详细的解决方案包括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())
}