Option 模式?
在调用函数的时候,可能参数比较多,但是我们只需要修改其中的几个特定参数就可以满足我们的需求,而其他的参数我们希望保持不变,但是 Rust 和 Go 一样,函数调用中不支持默认参数,所以我们必须另寻他法,那么第一感觉我们是不是可以采用 Go 中的 选项模式呢?
虽然 Rust 函数中并不支持变参数传递,但是我们可以通过 宏
来间接达到相同的功能,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #[derive(Debug)] pub struct Server { host: String, port: i16, }
impl Default for Server { fn default() -> Self { Self { host: "127.0.0.1".to_string(), port: 8888, } } }
macro_rules! NewServer { ( $($f:ident), *) => {{ let mut s = Server { ..Default::default() };
$( $f(&mut s); )*
s }}; }
|
我们只要传递函数给宏,函数中修改参数的指即可,但是我们如何将函数传递进去呢,这里我们采用 FnOnce
闭包函数
1 2 3 4 5 6 7
| fn with_host(host: String) -> impl FnOnce(&mut Server) { move |server| server.host = host }
fn with_port(port: i16) -> impl FnOnce(&mut Server) { move |server| server.port = port }
|
调用的时候不太方便,因为宏中不支持使用括号,所以必须使用下面的方式进行调用
1 2 3 4 5 6 7
| #[test] fn test() { let f = with_host(String::from("0.0.0.0")); let f2 = with_port(8000); let s = NewServer![f, f2]; println!("{:?}", s); }
|
函数重载
在 Rust 中实际上并不支持函数重载,但是通过宏,我们可以达到类似的效果,但同时由于宏的替换方式,所以我们无法通过判断变量的类型,然后动态返回结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #[derive(Debug)] pub struct Server { host: String, port: i16, }
#[macro_export] macro_rules! NewServer { () => { Server { host: String::from("127.0.0.1"), port: 8080, } };
($var: expr) => { Server { host: String::from($var), port: 8080, } }; (_, $var: expr) => { Server { host: String::from("127.0.0.1"), port: $var, } }; }
#[test] fn test() { let s = NewServer!(); println!("{:?}", s); let s = NewServer!("0.0.0.0"); println!("{:?}", s); let s = NewServer!(_, 8888); println!("{:?}", s); }
|
手动实现 setter
创建对象的时候返回一个结构体,然后提供 setter
方法进行配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #[derive(Debug)] pub struct Server { host: String, port: i16, }
impl Server { pub fn new() -> Self { Self { host: String::from("127.0.0.1"), port: 8888, } }
pub fn host(mut self, host: &str) -> Self { self.host = host.to_string(); self }
pub fn port(mut self, port: i16) -> Self { self.port = port; self } }
#[test] fn test_new_server() { let server = Server::new().host("0.0.0.0").port(8080); println!("{:?}", server); }
|
使用 crate
自己手动写了一个 derive macro
,可以自动生成 getter
和 setter
,代码仓库见 construct,在 Cargo.toml
中进行引入:
1 2
| [dependencies] construct = {git = "https://github.com/junhaideng/construct"}
|
然后创建结构体的时候添加 derive(Constructor)
即可,详细使用方式见代码仓库说明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| mod server { use construct::Constructor;
#[derive(Debug, Constructor)] pub struct Server { #[cons(setter = false, rename_getter = get_host)] host: String, #[cons(getter = false, rename_setter = set_server_port)] port: u16, }
impl Server { pub fn new() -> Self { Self { host: String::from("127.0.0.1"), port: 8080, } } } }
#[test] fn test() { use crate::server::Server; let mut s = Server::new(); println!("host: {}", s.get_host());
s.set_server_port(10); println!("{:?}", s); }
|
Builder 模式
与之类似的方式,其实我们也可以通过 Builder
(创建者) 模式达到相同的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| struct Builder { host: String, port: u16, }
struct Server { host: String, port: u16, }
impl Builder { pub fn new() -> Self { Self::default() } pub fn host(mut self, host: &str) -> Self { self.host = host.to_string(); self } pub fn port(mut self, port: u16) -> Self { self.port = port; self } pub fn build(self) -> Server { Server { host: self.host, port: self.port, } } }
impl Default for Builder { fn default() -> Self { Self { host: "127.0.0.1".to_string(), port: 8080, } } }
|