Rust,使用枚举从HashMap获取值

pqwbnv8z  于 2023-03-02  发布在  其他
关注(0)|答案(2)|浏览(176)

我正在尝试创建一个具有不同类型的HashMap。我不想为特定数据类型创建两个不同的HashMap。
我的代码如下:

use std::collections::HashMap;

#[derive(Debug)]
enum DataTypes {
    String(String),
    Bool(bool),
}

fn get_hashmap() -> Result<HashMap<String, DataTypes>, ()>{
    let data = HashMap::from([
        ("password".to_string(), DataTypes::String("password".to_string())),
        ("username".to_string(), DataTypes::String("Fun username".to_string())),
        ("is_blocked".to_string(), DataTypes::Bool(true)),
        ("is_confirmed".to_string(), DataTypes::Bool(false)),
    ]);
    Ok(data)
}
fn main() {
    let data = get_hashmap().unwrap();
    let keys = data.keys();
    println!("Keys: {:?}", &keys);
    for key in keys {
        let result: Option<T> = match data.get(key).unwrap() {
            DataTypes::Bool(value) => Some(value),
            DataTypes::String(value) => Some(value),
            _ => panic!("Error!"),
        };
        println!("Result of matching: {:?}", &result);
    }
}

正如你所看到的,我正在尝试匹配枚举来获取它们的值。但是我有一些数据类型的问题。我的解决方案是 Package 匹配到一些结构的结果。但是主要问题仍然没有解决。
所以我想在Option类中做一个匹配的结果,使unwrap()可用。但是我不知道怎样才能正确地做...
我有两个问题:
1.我能做得更好吗?
1.我如何 Package 让结果:工作状态选项?

8hhllhi2

8hhllhi21#

一些反馈:

  • 如果你已经处理了所有的选项,就不要包含_默认匹配大小写,这会隐藏将来的错误。
  • 如果每个成员只有一个数据类型,就不要将变量命名为DataTypes,将其命名为DataType
  • result必须是一个特定的类型。枚举的全部意义在于你可以分别处理不同的值,所以把它们组合在一个result类型中是没有意义的。当然你可以result保留为DataType对象,并为它实现Debug/Display,这就是我在修改后的代码中要做的。
  • 虽然你可以先查询键,然后再查询循环中的值,但这是相当慢的。你可以立即迭代键值对。这样你就避免了很多unwrap(),这使得你的代码更不容易出错。
use std::collections::HashMap;

#[derive(Debug)]
enum DataType {
    String(String),
    Bool(bool),
}

fn get_hashmap() -> Result<HashMap<String, DataType>, ()> {
    let data = HashMap::from([
        (
            "password".to_string(),
            DataType::String("password".to_string()),
        ),
        (
            "username".to_string(),
            DataType::String("Fun username".to_string()),
        ),
        ("is_blocked".to_string(), DataType::Bool(true)),
        ("is_confirmed".to_string(), DataType::Bool(false)),
    ]);
    Ok(data)
}
fn main() {
    let data = get_hashmap().unwrap();
    for (key, value) in data {
        println!("{}: {:?}", key, value);

        match value {
            DataType::Bool(value) => {
                println!("\tValue was a bool: {}", value);
                // do something if the value is a bool
            }
            DataType::String(value) => {
                println!("\tValue was a string: {}", value);
                // do something if the value is a string,
            } /*
               * Don't include a default case. That way the compiler
               * will remind you to handle additional enum entries if
               * you add them in the future.
               * Adding a default case is only a good practice in languages
               * where matching is not exhaustive.
               */
        };
    }
}
username: String("Fun username")
        Value was a string: Fun username
is_confirmed: Bool(false)
        Value was a bool: false
is_blocked: Bool(true)
        Value was a bool: true
password: String("password")
        Value was a string: password

不过不用担心,你不需要在使用这个枚举的任何地方都使用match,否则与两个单独的散列表相比,你不会有太大的优势。相反,你可以为所有枚举条目定义共享功能,并将match隐藏在其中。
一个二个一个一个
如果您的目标是将序列化/反序列化添加到您的结构中(就像您在这里手动实现的那样),让我向您提示一下serde,它已经免费处理了大部分序列化。
就像在这个例子中(可能是也可能不是你的结构体的样子),它将你的结构体序列化为JSON或者从JSON序列化:

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct User {
    username: String,
    password: String,
    is_blocked: bool,
    is_confirmed: bool,
}

fn main() {
    let user = User {
        username: "Fun username".to_string(),
        password: "password".to_string(),
        is_blocked: true,
        is_confirmed: false,
    };

    let user_serialized = serde_json::to_string(&user).unwrap();
    println!("Serialized: {}", user_serialized);

    let user_deserialized: User = serde_json::from_str(&user_serialized).unwrap();
    println!("Name: {}", user_deserialized.username);
}
Serialized: {"username":"Fun username","password":"password","is_blocked":true,"is_confirmed":false}
Name: Fun username
kdfy810k

kdfy810k2#

感谢Finomnis我找到了我想要的方法。这就是我的解决方案。

use std::collections::HashMap;

#[derive(Debug)]
enum DataType {
    String(String),
    Bool(bool),
}

impl DataType {
    fn get_bool(&self) -> bool {
        let result = match self {
            DataType::Bool(value) => value.to_owned(),
            _ => panic!("Something"),
        };
        result
    }

    fn get_string(&self) -> String {
        let result = match self {
            DataType::String(value) => value.to_owned(),
            _ => panic!("Something"),
        };
        result
    }
}

fn get_hashmap() -> Result<HashMap<String, DataType>, ()>{
    let data = HashMap::from([
        ("password".to_string(), DataType::String("password".to_string())),
        ("username".to_string(), DataType::String("Fun username".to_string())),
        ("is_blocked".to_string(), DataType::Bool(true)),
        ("is_confirmed".to_string(), DataType::Bool(false)),
    ]);
    Ok(data)
}

fn main() {
    // now we can simply get values from hashmap
    let mut my_hash_map = get_hashmap().unwrap();
    let username = my_hash_map.remove("username").unwrap().get_string();
    let is_blocked = my_hash_map.remove("is_blocked").unwrap().get_bool();

    // Now we have clear value of username and is_blocked.
    println!("{}", username);
    println!("{}", is_blocked);
}

我认为当我们想要使用数据库时,当我们不想序列化所有的用户结构时,这会很有帮助。
也许这可以做得更好。再次感谢大家的大力帮助!:)

相关问题