作为一种编译式语言,在代码组织层面怎么做到既流畅又优雅呢? 比如像下面这种写法参考 sqlx 链接池的配置
let conn = MySqlPoolOptions::new()
// max connections
.max_connections(cfg.max_conn)
//
.max_lifetime(Duration::from_secs(59))
// client connect max idle
.idle_timeout(Duration::from_secs(60))
// connect db
.connect(&cfg.dsn)
.await
.with_context(|| format!("connect db {}", cfg.dsn))?;
分析 max_conn** 方法 类似于 接受参数为 self 并且返回一个 Self
pub fn idle_timeout(mut self, timeout: impl Into<Option<Duration>>) -> Self {
self.idle_timeout = timeout.into();
self
}
照猫画虎,在 http 短链服务中,我们都需要确定一个固定的基本的结构,然后加上可变的部分, 比如
{
"err_no": 10000,
"err_msg": "success",
"data": {
/// many key and values
}
}
根据以上需求, 我们很容易确定
#[derive(Debug)]
pub struct AppErr<T: serde::Serialize> {
err_no: i64,
err_msg: String,
data: Option<T>,
}
但是,有一个问题,因为在编写新构造 AppErr 对象的时候就要确定 泛型参数T的类型, 但是在很多情况下,我们只是 data 字段的值和类型不一样, 这个时候,为了减少重复代码的编写, 我们需要增加
impl<T: Serialize> AppErr<T> {
pub fn new(err_no: i64, err_msg: &str) -> Self {
let data: Option<T> = None;
Self { err_msg: err_msg.to_owned(), err_no, data }
}
pub fn with_data<F: serde::Serialize>(self, data: Option<F>) -> AppErr<F> {
AppErr { data, err_no: self.err_no, err_msg: self.err_msg }
}
}
with_data 方法返回一个新类型的 AppErr 对象,并且 data 字段完全有可能不同于前一个 data 的类型。
完整的代码如下:
#[derive(Debug)]
pub struct AppErr<T: serde::Serialize> {
err_no: i64,
err_msg: String,
data: Option<T>,
}
impl<T: Serialize> AppErr<T> {
pub fn new(err_no: i64, err_msg: &str) -> Self {
let data: Option<T> = None;
Self { err_msg: err_msg.to_owned(), err_no, data }
}
pub fn with_data<F: serde::Serialize>(self, data: Option<F>) -> AppErr<F> {
AppErr { data, err_no: self.err_no, err_msg: self.err_msg }
}
pub fn with_err_no(self, err_no: i64) -> Self {
Self { err_no, ..self }
}
pub fn with_err_msg(self, err_msg: &str) -> Self {
Self { err_msg: err_msg.to_owned(), ..self }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let data = AppErr::<i32>::new(1000, "foo/baz").with_data(Some("hello"));
println!("data {:?}", data);
// let data = data.with_err_no(999);
// println!("data {data:?}",);
// let data = data.with_err_msg("test/case");
// println!("data {data:?}",);
let data = data.with_data(Some(233));
println!("data {data:?}",);
}
}