rust - 当提取函数的返回类型是根据特征定义的时,如何在 Rust 中提取函数的一部分?
问题描述
问题
我将 Rust 与 vscode 以及“Rust and Friends v1.0.0”引入的插件一起使用。
我想使用提取函数技术重构一个长函数,但在某些情况下,IDE 无法确定提取函数的返回类型。
我认为原因是该类型是根据特征描述的,并且不可能将该类型定义为返回类型。
由于我是 Rust 的新手,并且我希望我的评估不准确,因此我将提供一个示例。
例子
我正在使用回形针板条箱来设置 REST 服务器。配置服务器的部分如下所示:
let server = HttpServer::new(move || {
let app = App::new()
.wrap(Logger::default())
.wrap_api()
.data(pool.clone());
let app = app.service(
web::scope(“/api”).service(
web::scope(“/customers”).service(
web::resource(“/transactions”)
.route(web::get().to(schema_handlers::get_transactions))
.route(web::post().to(schema_handlers::add_transaction)),
),
),
);
let app = app.service(
web::scope(“/api”).service(
web::scope(“/admin”).service(
web::resource(“/permissions”)
.route(web::get().to(schema_handlers::get_permissions))
.route(web::post().to(schema_handlers::add_permission)),
),
),
);
app.with_json_spec_at("/api/spec").build()
})
.bind(format!("0.0.0.0:{}", port))?
.run();
回形针支持流畅的 API,因此可以链接所有服务定义,但我更愿意为我添加的每个处理程序范围提取一个函数。
这就是为什么我最初将单个流利调用拆分为两个单独的任务。
下一步是将每个let app = app.service (
语句提取到一个函数中。
但要做到这一点,我需要能够表达一个特征的类型app
或至少一个特征的名称,该特征在service
此处使用该方法时公开该方法。
在这种情况下,IDE 无法检测到类型。
当我使用Rust 中的“let”类型技巧和 IDE 中的一些提示时,我得出的结论是该类型是:
App<impl ServiceFactory<Config = (), Request = ServiceRequest, Response = ServiceResponse<StreamLog<Body>>, Error = Error, InitError = ()>, StreamLog<Body>>
此类型不能显式用于限定app
变量,也不能用作提取函数的返回类型,该函数将替换赋值的右侧app
。
从编译器错误消息中,我了解到类型表达式中存在特征(如impl
关键字的存在所示)是导致此问题的原因。
另一个问题是这种类型规范非常长而且冗长。
我可以通过类型别名来解决冗长问题,但是编译抱怨impl
类型别名不稳定,这在我看来是归结为同样的问题。
从示例中学习
在我看来,在某些情况下类型定义良好并且可以由编译器推断,但是由于它们包含特征定义,它们不能(容易)显式编写,因此extract function
重构方法并不总是可行的。
在我看来,这似乎是语言的一个重大限制。
今天有没有办法可以提取函数(无需等待 Rust 中的特征别名)?
解决方案
我假设您希望重构来改变这一点:
let app = app.service(
web::scope("/api").service(
web::scope("/customers").service(
web::resource("/transactions")
.route(web::get().to(schema_handlers::get_transactions))
.route(web::post().to(schema_handlers::add_transaction)),
),
),
);
变成这样的东西:
fn add_transaction_routes(app: App) -> App {
app.service(
web::scope("/api").service(
web::scope("/customers").service(
web::resource("/transactions")
.route(web::get().to(schema_handlers::get_transactions))
.route(web::post().to(schema_handlers::add_transaction)),
),
),
)
}
let app = add_transaction_routes(app);
当然这不起作用,因为App
它是通用的并且不完整。您可以impl Trait
像这样使用参数和返回类型:
fn add_transaction_routes(
app: App<impl ServiceFactory<...>, Body>,
) -> App<impl ServiceFactory<...>, Body> {
但我认为这是一种轻微的误用。虽然它本身可能不正确,但impl Trait
s 是单独推导出来的。函数签名表明app
传入的 可能与返回的类型不同,但.service()
实际上返回Self
。所以让它成为一个简单的通用函数会更合适:
fn add_transaction_routes<T>(app: App<T, Body>) -> App<T, Body>
where
T: ServiceFactory<...>,
{
然后,您可以选择制作一个超级特征以减少调用所需的样板.service()
:
ServiceFactory<
ServiceRequest,
Config = (),
Response = ServiceResponse<Body>,
Error = Error,
InitError = (),
>
但这一切都开始变得混乱。虽然有可能,但很明显这种类型的组织并不是专门为这种组织设计的。相反,我建议您的重构是围绕制作服务而不是围绕将它们添加到App
. 我认为这更清楚:
fn transaction_routes() -> impl HttpServiceFactory {
web::scope("/api").service(
web::scope("/customers").service(
web::resource("/transactions")
.route(web::get().to(schema_handlers::get_transactions))
.route(web::post().to(schema_handlers::add_transaction)),
),
)
}
let app = app.service(transaction_routes());
推荐阅读
- sql - 如何 PIVOT SQL 表
- android - android clip儿童不工作cornerRadius形状
- mysql - 从大型 IEnumerable 中获取计数
- hadoop - 如何从在 Ubuntu 16.04 上运行的 hadoop 3.0.3 卸载 sqoop-1.99.7
- javascript - 列包装jsPDF自动表不起作用?
- flutter - 在创建带有浮动应用栏的应用时使用 StreamBuilder
- c++ - 内存分配代码块
- mqtt - 如何将数据添加到 msg.payload 以通过 MQTT 发送?
- php - 登录表单数据库检查错误
- c++ - Visual Studio 2017,智能感知不工作