我是 Rust 新手,但是是从 C++ 角度/背景接触 Rust 的编程老手,所以这可以解释我的大部分困惑。
我的核心是一个需要解决的简单得令人麻木的问题。然而,生锈中看似最基本的东西却让我陷入了麻烦的老鼠窝。
我有一些代码:
pub fn my_application(cfg: &mut web::ServiceConfig) {
let auth = HttpAuthentication::bearer(validate_bearer);
...
我想重构它。我想使用我自己的自定义中间件,而不是使用“actix”HttpAuthentication::bearer 中间件。
所以所有我想做的就是采用“let auth = ...”行并将等号后面的内容放入另一个文件中的函数中。
这有多难?
所以我尝试了显而易见的方法: 在新文件中:
// WONT repeat 'use' statements in rest, but included once in case you try to compile)
use actix_web::dev::ServiceRequest;
use actix_web::Error;
use actix_web_httpauth::extractors::bearer::BearerAuth;
use actix_web_httpauth::middleware::HttpAuthentication;
use core::future::Future;
use crate::server::auth::common::validate_bearer;
// in C++, you would might say auto, or some such
pub fn create_auth_middleware_1()
{
let auth = HttpAuthentication::bearer(validate_bearer);
auth
}
失败了:
^^^^ expected `()`, found `HttpAuthentication<BearerAuth, ...>`
好的 - 所以 Rust 没有函数声明的自动或类型推导(或者我错过了它的语法)。很好。
接下来我尝试了“泛型”
pub fn create_auth_middleware_2<T>() -> T
{
let auth = HttpAuthentication::bearer(validate_bearer);
auth
}
结果是:
^^^^ expected type parameter `T`, found `HttpAuthentication<BearerAuth, ...>`
Next - 受服务“wrap”函数声明的启发,在 T 上尝试了一系列“where”子句来指定它——这是一个非常复杂的类型声明。例如:
// ONE TIME ADD 'uses' but keep around for other samples that need it...
use actix_service::boxed::{BoxService};
use actix_web::body::MessageBody;
use actix_web::dev::{Payload, Service, ServiceResponse, Transform};
use actix_web::http::header::Header;
use actix_web::{FromRequest, HttpRequest};
pub fn create_auth_middleware_3<T>() -> T
where T: Transform<S, ServiceRequest>,
S: Service<ServiceRequest, Response = ServiceResponse, Error = Error> {
let auth = HttpAuthentication::bearer(validate_bearer);
auth
}
这次也许是来自编译器的提示!
auth
| ^^^^ expected type parameter `T`, found `HttpAuthentication<BearerAuth, ...>`
|
= note: expected type parameter `T`
found struct `HttpAuthentication<BearerAuth, fn(..., ...) -> ... {validate_bearer}>`
the full type name has been written to ....
所以我尝试了编译器写入该文件的实际类型
pub fn create_auth_middleware_4() -> HttpAuthentication<BearerAuth, fn(ServiceRequest, BearerAuth) -> impl futures::Future<Output = std::result::Result<ServiceRequest, actix_web::Error>> {validate_bearer}> {
let auth = HttpAuthentication::bearer(validate_bearer);
auth
}
结果:
error[E0747]: constant provided when a type was expected
--> src/server/auth/middleware.rs:37:69
|
37 | ...h, fn(ServiceRequest, BearerAuth) -> impl futures::Future<Output = std::result::Result<ServiceRequest, actix_web::Error>> {validate_bearer}...
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
好吧 - 也许 Rust 不喜欢它作为类型的一部分编写的显式
{validate_bearer}
内容。
pub fn create_auth_middleware_5() -> HttpAuthentication<BearerAuth, fn(ServiceRequest, BearerAuth) -> impl futures::Future<Output = std::result::Result<ServiceRequest, actix_web::Error>> > {
let auth = HttpAuthentication::bearer(validate_bearer);
auth
}
结果:
error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in `fn` pointer return types
我正在尝试做的这件事 - 将表达式包装到另一个文件中的单独函数中,对于我曾经使用过的几乎所有编程语言(Python,prolog,C,C ++,Java,Typescript, JavaScript、Ruby、Lisp)。我确信 Rust 中也有一种非常简单的方法可以做到这一点,但我没有找到它。请帮助我看看我缺少什么,以及如何对 Rust 代码进行这种基本分解。
理想的解决方案是执行
impl Fn(...) -> impl Future<...>
,但像这样使用 impl
两次是目前不允许的。 另一个线程中提到了一个适用于此问题的解决方法。希望将来可以通过完全允许或通过 type_alias_impl_trait
来解决这个问题。现在,您可以通过创建新特征来解决这个问题。 (游乐场)
pub trait BearerFuture: Fn(ServiceRequest, BearerAuth) -> Self::Fut {
type Fut: Future<Output = Result<ServiceRequest, (Error, ServiceRequest)>>;
}
impl<F, Fut> BearerFuture for F
where
F: Fn(ServiceRequest, BearerAuth) -> Fut,
Fut: Future<Output = Result<ServiceRequest, (Error, ServiceRequest)>>,
{
type Fut = Fut;
}
pub fn create_auth_middleware_5() -> HttpAuthentication<BearerAuth, impl BearerFuture> {
HttpAuthentication::bearer(validate_bearer)
}
有一些夜间功能可以让这些变得更容易。由于这些尚未进入稳定状态,因此距离稳定状态(今天发布 1.70)至少还需要 12 周,并且可能需要 6 个月或更长时间。我希望
type_alias_impl_trait
首先发布,最终发布的可能性很大,因为它对于 async
代码非常有用,并且似乎涵盖了 impl_trait_in_fn_trait_return
的用途。我提到这些主要是为了未来的读者或已经在使用 nightly 的读者。其他人应该使用新的特征方法。
有了
type_alias_impl_trait
,你就可以做到这一点。 (游乐场)
#![feature(type_alias_impl_trait)]
type BearerFn = impl Fn(ServiceRequest, BearerAuth) -> BearerFuture;
type BearerFuture = impl Future<Output = Result<ServiceRequest, (Error, ServiceRequest)>>;
pub fn create_auth_middleware_5() -> HttpAuthentication<BearerAuth, BearerFn> {
HttpAuthentication::bearer(validate_bearer)
}
并且使用
impl_trait_in_fn_trait_return
你可以制造出这种丑陋的混乱。 (游乐场)
#![feature(impl_trait_in_fn_trait_return)]
pub fn create_auth_middleware_5() -> HttpAuthentication<
BearerAuth,
impl Fn(
ServiceRequest,
BearerAuth,
) -> impl Future<Output = Result<ServiceRequest, (Error, ServiceRequest)>>,
> {
HttpAuthentication::bearer(validate_bearer)
}
注意:您几乎永远不会在 Rust 中使用
fn
类型。您通常会使用 Fn
/FnMut
/FnOnce
特征之一。