I'm trying make one HashMap with different types. I don't want to make two different HashMaps for specific data types.
My code is bellow:
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);
}
}
Like you can see I'm trying to maching Enums to getting their values. But i have some problem of data types. My solution for this is wrap result of matching to Some struct. But still main problem is not resolved.
So I want to make result of matching in Option class to make available unwrap(). But I don't know how i can do that correctly...
I have two question:
Some feedback:
_ default match case if you already handle all the options. It will hide future errors.DataTypes if every member is only a single datatype. Name it DataType.result has to be a specific type. The whole point of the enum is that you can handle the different values separately, so combining them in a result type is pointless. Although you of course can keep result a DataType object and implement Debug/Display for it, which is how I will do it in my reworked code.unwrap()s, which makes your code a lot less error-prone.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
Don't worry though, you don't need to use match everywhere you use this enum, otherwise you wouldn't win much compared to two separate hashmaps. You can, instead, define shared functionality for all enum entries, and hide the match inside of it. Like this:
use std::collections::HashMap;
#[derive(Debug)]
enum DataType {
String(String),
Bool(bool),
}
impl DataType {
fn do_something(&self) {
match self {
DataType::Bool(value) => {
println!("\tDo something with boolean '{}'!", value);
}
DataType::String(value) => {
println!("\tDo something with string {:?}!", value);
}
};
}
}
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);
value.do_something();
}
}
is_confirmed: Bool(false)
Do something with boolean 'false'!
password: String("password")
Do something with string "password"!
is_blocked: Bool(true)
Do something with boolean 'true'!
username: String("Fun username")
Do something with string "Fun username"!
If your goal is to add serialization/deserialization to your struct (as you seem to implement manually here), let me hint you towards serde, which already takes care of the majority of serialization for free.
Like in this example (which may or may not be how your struct looks like) that serializes your struct to and from 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With