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 , } } }