8

Rust 参数设置默认值

 2 years ago
source link: https://junhaideng.github.io/2022/07/27/rust/default/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
1 个月前发表Rust / Note5 分钟读完 (大约817个字)3 次访问

Rust 参数设置默认值

Option 模式?

在调用函数的时候,可能参数比较多,但是我们只需要修改其中的几个特定参数就可以满足我们的需求,而其他的参数我们希望保持不变,但是 Rust 和 Go 一样,函数调用中不支持默认参数,所以我们必须另寻他法,那么第一感觉我们是不是可以采用 Go 中的 选项模式呢?

虽然 Rust 函数中并不支持变参数传递,但是我们可以通过 来间接达到相同的功能,代码如下

#[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 闭包函数

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
}

调用的时候不太方便,因为宏中不支持使用括号,所以必须使用下面的方式进行调用

#[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 中实际上并不支持函数重载,但是通过宏,我们可以达到类似的效果,但同时由于宏的替换方式,所以我们无法通过判断变量的类型,然后动态返回结果

#[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 方法进行配置。

#[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,可以自动生成 gettersetter,代码仓库见 construct,在 Cargo.toml 中进行引入:

[dependencies]
construct = {git = "https://github.com/junhaideng/construct"}

然后创建结构体的时候添加 derive(Constructor) 即可,详细使用方式见代码仓库说明。

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());

// not implement because rename
// println!("port: {}", s.port());
s.set_server_port(10);
println!("{:?}", s);
}

Builder 模式

与之类似的方式,其实我们也可以通过 Builder(创建者) 模式达到相同的功能。

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


生活杂笔,学习杂记,偶尔随便写写东西。


Recommend

  • 73

    PIN 码专用输入控件,支持更改 PinView 中每一个 EditText 的输入前、输入后、正在输入的 background 支持设置任意长度的 PIN 码长度 支持设置可输入的 PIN 码格式,目前支持文本、数字、可以自己自主设置,支持设置输入错误次数限制时间,支持设置输入的 PIN 码是...

  • 39

    Python语句中可以进行函数调用来简化工作,每个函数都可以完成具体的任务。当遇到相同任务,调用函数方便快捷。函数首先需设定形参(抽象概念),最后赋予具体值(实参),有些具体值可以不变,则可提前设定好默认值。eg.编写一个和T-SHIRT尺码及标语相关的函数:...

  • 53

    问题】 项目中每个请求的【服务器名称/IP】【协议】【端口号】【编码】都是相同的,由于请求数量多(70多个请求),要一个个重复填写或者修改,工作量很大,而且很枯燥 【解决】 在线程...

  • 20

    在实际开发中,会定义一些公共字段,而这些公共字段,一般都是在进行操作的时候由程序自动将默认值插入。而公共的字段一般会被封装到一个基础的实体类中,同时实体类中会实现相应的getter setter 方法 (注:如果使用了Lombok 插件,就没有getter set...

  • 50

    题记: 技术交流群中有小伙伴提及:“es 节点默认1000 个分片的限制”?这引发了我对Elasticsearch 默认值的关注。

  • 42
    • 微信 mp.weixin.qq.com 4 years ago
    • Cache

    Golang技巧之默认值的设置

    最近使用 GRPC 发现一个设计特别好的地方,非常值得借鉴。 我们在日常写方法的时候,希望给某个字段设置一个默认值,不需要定制化的场景就不传这个参数,但是 Golang 却没有提供像 ...

  • 50
    • www.banbeichadexiaojiubei.com 4 years ago
    • Cache

    Python字典(dict)设置默认值

    Python中的collections.defaultdict([default_factory[, … ]])在设置的字典key第一次出现的时候,自动调用default_factory方法创建一个默认的对象。这个类在实际使用中非常有用,可以帮助我们简化不少问题。 1.合并键...

  • 18

    pg表级参数修改后怎么简单恢复默认值? ...

  • 9

    Python函数参数默认值的陷阱和原理深究 本文将介绍使用mutable对象作为Python函数参数默认值潜在的危害,以及其实现原理和设计目的 我们就用实际...

  • 8

    小心此坑:Python 函数参数的默认值是可变对象 作者:somenzz 2022-11-14 07:08:23 Python 函数也是对象,参数的默认值就是对象的属性,在编译阶段参数的默认值就已经绑定到该函数,如果是可变对象,Python 函数参...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK