decltype、dyn、impl 特征,以及重构时如何声明函数的返回类型

问题描述 投票:0回答:1

我是 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 代码进行这种基本分解。

rust decltype actix-web
1个回答
6
投票

理想的解决方案是执行

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
特征之一。

© www.soinside.com 2019 - 2024. All rights reserved.