当根据特征定义提取函数的 return 类型时,如何提取 Rust 中的一部分函数?
How can I extract a part of function in Rust when the return type of the extracted function is defined in terms of a trait?
问题
我正在使用 Rust 和 vscode 以及“Rust and Friends”引入的插件
v1.0.0".
我想使用提取函数技术重构一个长函数,但在某些情况下 IDE 无法确定提取函数的 return 类型。
我认为原因是该类型是根据特征描述的,不可能将该类型定义为 return 类型。
由于我是 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 支持流畅的 API 以便可以链接所有服务定义,但我更愿意为我添加的每个处理程序范围提取一个函数。
这就是为什么我最初将单个流利调用分成两个单独的作业的原因。
下一步是将每个 let app = app.service (
语句提取到一个函数中。
但要这样做,我需要能够表达 app
的类型,或者至少是公开 service
方法的特征的名称,因为它在这里使用。
在这种情况下,IDE 无法检测类型。
当我使用 The “let” type trick in Rust 和 IDE 中的一些提示时,我得出的结论是类型是:
App<impl ServiceFactory<Config = (), Request = ServiceRequest, Response = ServiceResponse<StreamLog<Body>>, Error = Error, InitError = ()>, StreamLog<Body>>
此类型不能显式用于限定 app
变量,也不能用作提取函数的 return 类型,该类型将替换对 [=14 的赋值的右侧=].
根据编译器错误消息,我了解到类型表达式中存在特征(如 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
作为参数和 return 类型,如下所示:
fn add_transaction_routes(
app: App<impl ServiceFactory<...>, Body>,
) -> App<impl ServiceFactory<...>, Body> {
但我认为这是轻微的误用。虽然 本身可能并不正确 ,但 impl Trait
是单独推导出来的。函数签名表示传入的 app
可能与 return 的类型不同,但 .service()
实际上是 return 和 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());
问题
我正在使用 Rust 和 vscode 以及“Rust and Friends”引入的插件 v1.0.0".
我想使用提取函数技术重构一个长函数,但在某些情况下 IDE 无法确定提取函数的 return 类型。
我认为原因是该类型是根据特征描述的,不可能将该类型定义为 return 类型。
由于我是 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 支持流畅的 API 以便可以链接所有服务定义,但我更愿意为我添加的每个处理程序范围提取一个函数。
这就是为什么我最初将单个流利调用分成两个单独的作业的原因。
下一步是将每个 let app = app.service (
语句提取到一个函数中。
但要这样做,我需要能够表达 app
的类型,或者至少是公开 service
方法的特征的名称,因为它在这里使用。
在这种情况下,IDE 无法检测类型。
当我使用 The “let” type trick in Rust 和 IDE 中的一些提示时,我得出的结论是类型是:
App<impl ServiceFactory<Config = (), Request = ServiceRequest, Response = ServiceResponse<StreamLog<Body>>, Error = Error, InitError = ()>, StreamLog<Body>>
此类型不能显式用于限定 app
变量,也不能用作提取函数的 return 类型,该类型将替换对 [=14 的赋值的右侧=].
根据编译器错误消息,我了解到类型表达式中存在特征(如 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
作为参数和 return 类型,如下所示:
fn add_transaction_routes(
app: App<impl ServiceFactory<...>, Body>,
) -> App<impl ServiceFactory<...>, Body> {
但我认为这是轻微的误用。虽然 本身可能并不正确 ,但 impl Trait
是单独推导出来的。函数签名表示传入的 app
可能与 return 的类型不同,但 .service()
实际上是 return 和 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());