При попытке реализовать простое приложение веб-сервера с использованием actix-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
была удовлетворена в первом случае, но не во втором? Может быть, это вина фабрики (ее источники здесь )? Есть ли другой способ вернуть закрытие, которое будет работать в этой ситуации? Любые другие подходы, которые вы могли бы предложить? (Обратите внимание, что Factory
не является общедоступным, поэтому я не могу реализовать его непосредственно сам)
Если вы хотите дурачиться с кодом, он у меня здесь: https://github.com/yanivmo/rust-closure-experiments Обратите внимание, что вы можете перемещаться междуКоммит фиксирует код в рабочем или ошибочном состоянии.