我是一名Rust新手,但从C++的Angular /背景来看,我是一名编程老手,所以这可能解释了我的大部分困惑.

在它的核心,我有一个令人麻木的简单问题要解决.然而,铁 rust 中看似最基本的东西却让我陷入了麻烦的老鼠窝.

我有一些代码:

pub fn my_application(cfg: &mut web::ServiceConfig) {
   let auth = HttpAuthentication::bearer(validate_bearer);
   ...

我希望对其进行重构.我想要使用我自己的定制中间件,而不是使用‘Actix’HttpAuthentication::Beader中间件.

所以all我想做的就是把‘let auth=...’行,并将等号后面的内容放入另一个文件中的一个函数中.

How hard could that be?

因此,我try 了显而易见的方法: 在新文件中:

// 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, ...>`

OK-所以Rust没有函数声明的自动或类型演绎(或者我错过了这方面的语法).很好.

接下来,我try 了"泛型".

pub fn create_auth_middleware_2<T>() -> T
 {
    let auth = HttpAuthentication::bearer(validate_bearer);
    auth
}

这将导致:

^^^^ expected type parameter `T`, found `HttpAuthentication<BearerAuth, ...>`

Next--try 了T上的一系列‘where’子句来指定它,灵感来自于服务‘print’函数上的声明--这是一个非常复杂的类型声明.例如:

// 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 ....

所以我try 了编译器写入该文件的实际类型

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、TypeScrip、JavaScript、Ruby、Lisp)中都是微不足道的.我相信在Rust中也有一个非常简单的方法来做到这一点,但我没有找到它.请帮我看看我遗漏了什么,以及如何对Rust代码进行这种基本的因式分解.

推荐答案

理想的解决方案是使用impl Fn(...) -> impl Future<...>,但像这样使用impl两次就是currently not allowed.another thread中提到了一种解决方法,可以解决此问题.这个问题有望通过完全允许或在future 通过type_alias_impl_trait解决.现在,你可以通过创造一个新的特征来解决这个问题.(playground)

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)
}

有一些夜间功能可以让这一切变得更容易.由于这些版本还没有进入稳定状态,它们距离稳定状态至少还有12周的时间(今天发布的版本是1.70),而且可能需要6个月或更长时间.我预计type_alias_impl_trait将首先发布,最终发布的可能性很高,因为它对async代码非常有用,似乎涵盖了impl_trait_in_fn_trait_return的用法.我主要是为future 的读者或那些已经在每晚使用的读者提到这些.其他人应该使用新的trait 方法.

有了type_alias_impl_trait,你可以做到这一点.(playground)

#![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,你可能会制造出这个丑陋的烂摊子.(playground)

#![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个特征中的一个.

Rust相关问答推荐

在函数内定义impl和在函数外定义impl的区别

为什么Rust函数的移植速度比C++慢2倍?

在执行其他工作的同时,从共享裁判后面的VEC中删除重复项

无法将记录器向下转换回原始 struct

铁 rust 中的共享对象实现特征

为什么这是&q;,而让&q;循环是无限循环?

为什么Deref类特征不构成?

在 Rust 中用问号传播错误时对类型转换的困惑?

为什么编译器看不到这个 `From` impl?

使用占位符获取用户输入

try 从标准输入获取用户名和密码并删除 \r\n

sha256 摘要仅适用于 &*

有没有办法隐式绑定 let/match 操作的成员?

使用 lalrpop 在 rust 中解析由 " 引用的字符串

为什么不可变特征的实现可以是可变的?

具有在宏扩展中指定的生命周期的枚举变体数据类型

类型判断模式匹配panic

是否可以在 Rust 中的特定字符上实现特征?

如何解析 Rust 中的 yaml 条件字段?

在 Rust 中返回对枚举变体的引用是个好主意吗?