lew*_*wis 4 decltype rust actix-web
我是 Rust 新手,但是是从 C++ 角度/背景接触 Rust 的编程老手,所以这可以解释我的大部分困惑。
我的核心是一个需要解决的简单得令人麻木的问题。然而,生锈中看似最基本的东西却让我陷入了麻烦的老鼠窝。
我有一些代码:
pub fn my_application(cfg: &mut web::ServiceConfig) {
let auth = HttpAuthentication::bearer(validate_bearer);
...
Run Code Online (Sandbox Code Playgroud)
我想重构它。我想使用我自己的自定义中间件,而不是使用“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
}
Run Code Online (Sandbox Code Playgroud)
那失败了:
pub fn my_application(cfg: &mut web::ServiceConfig) {
let auth = HttpAuthentication::bearer(validate_bearer);
...
Run Code Online (Sandbox Code Playgroud)
好的 - 所以 Rust 没有函数声明的自动或类型推导(或者我错过了它的语法)。美好的。
接下来我尝试了“泛型”
pub fn create_auth_middleware_2<T>() -> T
{
let auth = HttpAuthentication::bearer(validate_bearer);
auth
}
Run Code Online (Sandbox Code Playgroud)
结果是:
// 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
}
Run Code Online (Sandbox Code Playgroud)
接下来 - 受服务“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
}
Run Code Online (Sandbox Code Playgroud)
这次也许是来自编译器的提示!
^^^^ expected `()`, found `HttpAuthentication<BearerAuth, ...>`
Run Code Online (Sandbox Code Playgroud)
所以我尝试了编译器写入该文件的实际类型
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
}
Run Code Online (Sandbox Code Playgroud)
结果是:
pub fn create_auth_middleware_2<T>() -> T
{
let auth = HttpAuthentication::bearer(validate_bearer);
auth
}
Run Code Online (Sandbox Code Playgroud)
好吧——也许 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
}
Run Code Online (Sandbox Code Playgroud)
结果是:
^^^^ expected type parameter `T`, found `HttpAuthentication<BearerAuth, ...>`
Run Code Online (Sandbox Code Playgroud)
我正在尝试做的这件事 - 将表达式包装到另一个文件中的单独函数中,对于我曾经使用过的几乎所有编程语言(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)
}
Run Code Online (Sandbox Code Playgroud)
有一些夜间功能可以让这些变得更容易。由于这些尚未进入稳定状态,因此距离稳定状态(今天发布 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)
}
Run Code Online (Sandbox Code Playgroud)
和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)
}
Run Code Online (Sandbox Code Playgroud)
注意:您几乎永远不会fn在 Rust 中使用该类型。Fn您通常会使用//FnMut特征FnOnce之一。