你如何在 Rust 的比赛中借用一个值?

how do you borrow a value during match in Rust?

我是 Rust 新手。

我创建了一个结构来保存系统信息。

pub struct systemConfig {
    pub admin_address: String,
    pub engine_name: Option<String>,
    pub group_name: Option<String>
}

我想将此结构传递给 make_msg 函数以创建一个 json 正文并将其作为请求发送到另一台服务器。

fn make_msg(config: systemConfig) -> String{
  let (host_name, cpus) = get_system_info();

  let engine_name = match config.engine_name {
    Some(name) => name,
    None => host_name.clone(),
  };

  let group_name = match config.group_name {
    Some(name) => name,
    None => String::from("")
  };

  let msg = json!({
    "engineName": engine_name,
    "groupName": group_name,
    "hostName": host_name,
  });

  msg.to_string()
}
   

 fn get_system_info() -> (String, usize){
  use sysinfo::{ System, SystemExt };

  // monitoring info
  let mut my_system = System::new_all();
  my_system.refresh_all();

  // hostname
  let hostname = get_hostname(&my_system);

  // logical cpu count
  let cpus = get_logical_cpus(&my_system);

  (hostname, cpus)
}

我有两个问题。

  1. engine_name和group_name是从process参数中得到的值。 type 被定义为 Option 的原因是它的值不是必需的。如果未输入引擎名称,则填写主机名。如果未输入组名,则将其作为“”(空字符串)发送。 我使用了 match 语法,但是有没有更合适的语法呢? (如果让 Some/None, 更简洁直观)
  2. None => host_name.clone(), 如果此处不执行clone(),则会出现借用问题。我正在寻找有关使用 clone() 是否正确的方法,或者是否有更好的方法的建议。

我添加测试代码

//cargo.toml
[dependencies]
    sysinfo = "0.23.12"
    serde_json = { version = "1.0", features = ["arbitrary_precision"] }

use sysinfo::{System, SystemExt};
use serde_json::json;

struct systemConfig {
    pub admin_address: String,
    pub engine_name: Option<String>,
    pub group_name: Option<String>
}

fn main() {
    let config = systemConfig {
        admin_address: String::from("127.0.0.1:8080"),
        engine_name: Some(String::from("hello")),
        group_name: Some(String::from("world"))
    };

    let msg = make_msg(config);
    println!("msg: {}", msg);
}

fn make_msg(config: systemConfig) -> String{
    let host_name = get_system_info();

    let engine_name = match config.engine_name {
        Some(name) => name,
        None => host_name.clone(),
    };

    let group_name = match config.group_name {
        Some(name) => name,
        None => String::from("")
    };

    let msg = json!({
    "engineName": engine_name,
    "groupName": group_name,
    "hostName": host_name,
  });

    msg.to_string()
}

fn get_system_info() -> String {
    use sysinfo::{ System, SystemExt };

    // monitoring info
    let mut my_system = System::new_all();
    my_system.refresh_all();

    // hostname
    let hostname = get_hostname(&my_system);

    hostname
}

pub fn get_hostname(s: &System) -> String {
    s.host_name().unwrap()
}

两个问题,两个答案:

  1. 展开或替换 Option 的情况很常见,它有自己的功能:Option::unwrap_or:
let engine_name = config.engine_name.unwrap_or(host_name.clone());
let group_name = config.group_name.unwrap_or(String::from(""));
  1. 克隆才是正道。在某些情况下,engineNamehostName 将包含相同的字符串,因此在任何时候都需要 .clone()

I used the match syntax, but is there a more appropriate syntax? (if let Some/None,, more concise and intuitive)

Option 有一些您可以使用的实用程序。在 engine_name 的情况下,unwrap_or_else() 比你的 match:

更简洁
let engine_name = config.engine_name
    .unwrap_or_else(|| host_name.clone());

对于 group_name 你可以使用 unwrap_or_default() 因为 DefaultString returns 上实现了一个空字符串:

let group_name = config.group_name.unwrap_or_default();

请注意,在这种情况下,这两个选项都优于 unwrap_or(),因为除非需要,否则它们不需要构建替代值。例如,在 engine_name 的情况下,这不会克隆 host_name 除非 config.engine_nameNone.

I'm looking for advice on whether using clone() is the right way, or if there is a better way.

您可以只使用像这样的引用使其工作:

let engine_name = match &config.engine_name {
  Some(ref name) => name,
  None => &host_name,
};

或者,像上面一样,您可以使用 unwrap_or()(结合 as_ref()):

let engine_name = config.engine_name.as_ref().unwrap_or(&host_name);

然而,JSON Value::String 变体需要一个拥有的字符串,所以不在这里克隆并不是真正的优化——json! 宏无论如何都会克隆它。