繁体   English   中英

当函数需要引用闭包参数时,如何使用函数作为闭包?

[英]How can I use a function as a closure when the function needs to take a reference to the closures argument?

以下代码可以正常工作( 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) -> _`

正确的反应方式是什么? 当然,我可以简单地执行#![cfg_attr(feature="clippy", allow(needless_pass_by_value))]但这对我来说是错的。 有没有一种方法可以将map_errshout版本一起使用进行参考?

最好的办法是使用完全闭包:

res.map_err(|x| shout(&x)).unwrap_err()

您的原始表单需要完成两个步骤:

  1. 它需要将参数带到闭包并将其转换为引用。
  2. 它需要将&String转换为&str

此外,它还需要在值处于范围内时执行这两项操作,以免最终导致悬挂引用。 这些都不是闭包的“短”形式现在要处理的东西-类型必须完全匹配。

如果您确实想避免关闭,可以针对此特定情况:

res.as_ref().map_err(String::as_str).map_err(shout).unwrap_err()
//  ^~~~~~           ^~~~~~~~~~~~~~
//  |                |
//  |                |- Convert `&String` to `&str`
//  |   
//  |- Get a reference (`&String`)   

我实际上是在为您的原始代码作为人体工程学计划的一部分工作的能力而争辩 ,但是似乎并没有引起人们的注意。


像编程中的许多问题一样,您可以通过添加更多抽象来“解决”这一问题。 在这里,我们引入一个特征来体现“可以大喊大叫的错误”的概念:

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不接受&String ,这将要求strSized类型。

更详细的解决方案涉及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())
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM