Dav*_*ein 2 refactoring traits rust type-alias
我将 Rust 与 vscode 以及“Rust and Friends\nv1.0.0”引入的插件一起使用。
\n我想使用提取函数技术重构一个长函数,但在某些情况下,IDE 无法计算出提取函数的返回类型。
\n我认为原因是该类型是根据特征来描述的,并且不可能将该类型定义为返回类型。
\n由于我是 Rust 新手,我预计我的评估不准确,因此我将提供一个示例。
\n我正在使用回形针箱来设置 REST 服务器。\n配置服务器的部分如下所示:
\nlet server = HttpServer::new(move || {\n let app = App::new()\n .wrap(Logger::default())\n .wrap_api()\n .data(pool.clone());\n\n let app = app.service(\n web::scope(\xe2\x80\x9c/api\xe2\x80\x9d).service(\n web::scope(\xe2\x80\x9c/customers\xe2\x80\x9d).service(\n web::resource(\xe2\x80\x9c/transactions\xe2\x80\x9d)\n .route(web::get().to(schema_handlers::get_transactions))\n .route(web::post().to(schema_handlers::add_transaction)),\n ),\n ),\n );\n \n let app = app.service(\n web::scope(\xe2\x80\x9c/api\xe2\x80\x9d).service(\n web::scope(\xe2\x80\x9c/admin\xe2\x80\x9d).service(\n web::resource(\xe2\x80\x9c/permissions\xe2\x80\x9d)\n .route(web::get().to(schema_handlers::get_permissions))\n .route(web::post().to(schema_handlers::add_permission)),\n ),\n ),\n );\n\n app.with_json_spec_at("/api/spec").build()\n})\n.bind(format!("0.0.0.0:{}", port))?\n.run();\nRun Code Online (Sandbox Code Playgroud)\npaperclip 支持流畅的 API,以便可以链接所有服务定义,但我更愿意为我添加的处理程序的每个范围提取一个函数。
\n这就是为什么我最初将单个流畅调用分成两个单独的作业。
\n下一步是将每个let app = app.service (语句提取到一个函数中。
但要做到这一点,我需要能够表达app公开该方法的特征的类型或至少是其名称service。
在这种情况下,IDE 无法检测类型。
\n当我使用Rust 中的 \xe2\x80\x9clet\xe2\x80\x9d 类型技巧以及 IDE 中的一些提示时,我得出的结论是该类型是:
\nApp<impl ServiceFactory<Config = (), Request = ServiceRequest, Response = ServiceResponse<StreamLog<Body>>, Error = Error, InitError = ()>, StreamLog<Body>>\nRun Code Online (Sandbox Code Playgroud)\n该类型不能显式地用于限定app变量,也不能用作提取函数的返回类型来替换赋值的右侧app。
从编译器错误消息中,我了解到类型表达式中存在特征(如关键字的存在所示)impl是导致此问题的原因。
另一个问题是这种类型规范非常长且冗长。
\n我可以通过类型别名来解决冗长的问题,但编译会抱怨impl类型别名不稳定,这在我看来归结为同一问题。
在我看来,在某些情况下,类型被很好地定义并且可以由编译器推断,但是因为它们包含特征定义,所以它们不能(容易)显式地编写,因此extract function重构方法并不总是可行的。
在我看来,这似乎是该语言的一个重大限制。
\n现在有没有一种方法可以提取函数(无需等待 Rust 中的特征别名)?
\n我假设您希望重构来改变这一点:
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)),
),
),
);
Run Code Online (Sandbox Code Playgroud)
变成这样的东西:
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);
Run Code Online (Sandbox Code Playgroud)
当然,这是行不通的,因为它App是通用的并且不完整。您可以impl Trait像这样使用参数和返回类型:
fn add_transaction_routes(
app: App<impl ServiceFactory<...>, Body>,
) -> App<impl ServiceFactory<...>, Body> {
Run Code Online (Sandbox Code Playgroud)
但我认为这是轻微的误用。虽然它本身可能不正确,但impl Traits 是单独推导的。函数签名表明app传入的 可能与返回的类型不同,但.service()实际上返回的是Self。因此,将其设为一个简单的通用函数会更合适:
fn add_transaction_routes<T>(app: App<T, Body>) -> App<T, Body>
where
T: ServiceFactory<...>,
{
Run Code Online (Sandbox Code Playgroud)
然后,您可以选择创建一个超级特征来减少调用所需的样板.service():
ServiceFactory<
ServiceRequest,
Config = (),
Response = ServiceResponse<Body>,
Error = Error,
InitError = (),
>
Run Code Online (Sandbox Code Playgroud)
但这一切开始变得混乱。虽然有可能,但很明显这种类型的组织并不是专门设计的。相反,我建议您的重构是围绕制作服务而不是围绕将它们添加到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());
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
453 次 |
| 最近记录: |