Я пытаюсь сделать один HashMap с разными типами. Я не хочу создавать два разных HashMaps для определенных типов данных.
Мой код ниже:
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);
}
}
Как вы можете видеть, я пытаюсь обработать Enums, чтобы получить их значения. Но у меня есть некоторые проблемы с типами данных. Мое решение для этого - обернуть результат сопоставления с некоторой структурой. Но до сих пор основная проблема не решена.
Итак, я хочу сделать результат сопоставления в классе Option, чтобы сделать доступным unwrap(). Но я не знаю, как это сделать правильно...
У меня два вопроса:
Я хочу отправить эти значения в базу данных. Перед отправкой данных в базу данных я хочу сериализовать результат в структуру пользователя.
Тогда почему вы не можете сериализовать их внутри match
?
Или просто сериализуйте перечисление напрямую (для serde, #[derive(Serialize)] #[serde(untagged)]
в перечислении).
Хороший вопрос, пока я не знаю, как это сделать, но я думаю, что это хорошая идея.
Некоторые отзывы:
_
по умолчанию, если вы уже обрабатываете все параметры. Это скроет будущие ошибки.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
внутри него. Так:
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"!
Если ваша цель — добавить сериализацию/десериализацию в свою структуру (как вы, кажется, реализуете здесь вручную), позвольте мне подсказать вам serde, которая уже заботится о большей части сериализации бесплатно.
Как в этом примере (который может быть или не быть тем, как выглядит ваша структура), который сериализует вашу структуру в 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
Спасибо за хороший отзыв. Это действительно полезно :) Мне нужно согласиться с вами во всех пунктах. Объединение всех типов данных в один результат имеет одну вескую причину. Мне нужно использовать его для отправки этого вывода в базу данных. У меня гораздо более сложная структура пользователя со свойствами Option<T>. Я хочу избежать сопоставления каждой записи в HashMap и получить значение из Enum. Но знайте, я вижу, что более простой метод используется для двух HashMaps с разными типами данных. Гораздо менее сложно.
Этот трюк с распаковкой хэш-карты в цикле for просто великолепен! Спасибо :)
@RfDzDeveloper Я не совсем согласен, я думаю, что использование перечисления нормально. Вы можете реализовать совместное поведение, скрывая операторы соответствия в реализации. Смотрите добавленный раздел в моем ответе.
Я думаю, что это отличный ответ! Теперь я ясно понимаю, что произошло и что я могу с этим сделать. Большое, большое спасибо за ваше время и объяснение мне этой концепции.
@RfDzDeveloper Но ChayimFriedman прав, если ваша проблема касается сериализации/десериализации, большая часть работы в Rust уже сделана, если вы используете serde. Хотя выполнение этого вручную, вероятно, также является хорошим упражнением для улучшения ваших навыков программирования.
@RfDzDeveloper Добавлен третий раздел, чтобы продемонстрировать, как serde может работать с вашей структурой.
Благодаря 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);
}
Я думаю, что это может быть полезно, когда мы хотим использовать базы данных. Когда мы не хотим сериализовать всю структуру пользователя.
Вероятно, это можно сделать намного лучше. Еще раз спасибо всем за БОЛЬШУЮ помощь! :)
Что ты хочешь сделать с
result
? Они разных типов.