【问题标题】:Returning closure from function inconsitent with defining in place从与就地定义不一致的函数返回闭包
【发布时间】:2020-02-28 07:54:46
【问题描述】:

在尝试使用 actix-web 实现一个简单的 Web 服务器应用程序时,我遇到了不知道如何解释的 Rust 闭包明显不一致的行为。

我有以下代码:

use actix_web::{web, App, HttpServer};

#[derive(Clone)]
struct Config {
    val1: String,
    val2: String,
    val3: String,
}

fn main() {
    let conf = Config {
        val1: "just".to_string(),
        val2: "some".to_string(),
        val3: "data".to_string(),
    };

    HttpServer::new(move ||
        App::new().configure(create_config(&conf))
    )
        .bind("127.0.0.1:8088")
        .unwrap()
        .run()
        .unwrap();
}

fn create_config<'a>(conf: &'a Config) -> impl FnOnce(&mut web::ServiceConfig) + 'a {
    move |app: &mut web::ServiceConfig| {
        // Have to clone config because web::get().to by definition requires
        // its argument to have static lifetime, which is longer than 'a
        let my_own_conf_clone = conf.clone();
        app.service(
            web::scope("/user")
                .route("", web::get().to(move || get_user(&my_own_conf_clone)))
        );
    }
}

fn get_user(conf: &Config) -> String {
    println!("Config {} is {} here!", conf.val3, conf.val1);
    "User McUser".to_string()
}

此代码有效。注意我传递给web::get().to 的闭包。我用它来将Config 对象传递给get_user,并且仍然存在web::get().to,它有一个没有参数的函数,因为它需要。此时我决定将闭包生成移到一个单独的函数中:

fn create_config<'a>(conf: &'a Config) -> impl FnOnce(&mut web::ServiceConfig) + 'a {
    move |app: &mut web::ServiceConfig| {
        app.service(
            web::scope("/user")
                .route("", web::get().to(gen_get_user(conf)))
        );
    }
}

fn gen_get_user(conf: &Config) -> impl Fn() -> String {
    let my_own_conf_clone = conf.clone();
    move || get_user(&my_own_conf_clone)
}

fn get_user(conf: &Config) -> String {
    println!("Config {} is {} here!", conf.val3, conf.val1);
    "User McUser".to_string()
}

此代码编译失败,出现以下错误:

error[E0277]: the trait bound `impl std::ops::Fn<()>: actix_web::handler::Factory<_, _>` is not satisfied
  --> src/main.rs:30:39
   |
30 |       .route("", web::get().to(gen_get_user(conf)))
   |                             ^^ the trait `actix_web::handler::Factory<_, _>` is not implemented for `impl std::ops::Fn<()>`

为什么它在第二种情况下失败,但在第一种情况下没有?为什么特征 Factory 在第一种情况下得到满足,但在第二种情况下不满足?可能是工厂(其来源是here)的错吗?有没有一种不同的方式来返回一个闭包,在这种情况下会起作用吗?您可以建议任何其他方法吗? (注意Factory不是公开的,所以我自己不能直接实现)

如果你想玩弄代码,我在这里:https://github.com/yanivmo/rust-closure-experiments 请注意,您可以在提交之间移动以查看处于工作状态或失败状态的代码。

【问题讨论】:

    标签: rust closures actix-web


    【解决方案1】:

    然后使用impl Trait 作为返回类型,除该值实现Trait 之外的所有其他类型信息都被删除。

    在这种特殊情况下,闭包 move || get_user(&amp;my_own_conf_clone) 实现了 Fn() -&gt; StringClone,但在返回 Clone 后会被删除。

    但由于FactoryFn() -&gt; String + Clone实现,而不为Fn() -&gt; String返回值不再实现工厂。

    这可以通过将gen_get_user 更改为来解决

    fn gen_get_user(conf: &Config) -> impl Fn() -> String + Clone{
        let my_own_conf_clone = conf.clone();
        move || get_user(&my_own_conf_clone)
    }
    

    【讨论】:

    • 你,先生,是我的英雄!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-18
    • 1970-01-01
    相关资源
    最近更新 更多