我将 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();
paperclip 支持 fluent API,因此可以链接所有服务定义,但我更愿意为我添加的每个处理程序范围提取一个函数。
这就是为什么我最初将单个 fluent 调用拆分为两个单独的任务。
下一步是将每个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 中的 trait 别名)?
我假设您希望重构来改变这一点:
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<...>,
{
然后,您可以选择创建一个 supertrait 以减少调用所需的样板.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());
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句