如何实现一个以str为键,以Trait为值的全局可变HashMap?
How to implement a global and mutable HashMap with str as key and Trait as values?
我正在尝试实现值键对,其键是字符串,值是实现某些预定义函数的对象类型。我遇到的问题是特征(我在这种情况下将其用作一种继承)没有预定义的大小并且不是线程安全的。
代码
创建特征和结构
/// _State line to define the basis of the 'State'
/// type with signatures of specific functions
pub trait _State {
fn as_string(&self, text: String) -> String;
fn print(&self);
}
/// Status type to add your custom colors
pub struct RGBState {
name: String,
color: (u8, u8, u8),
character: String,
}
/// Default state type using preconfigured ANSI colors
pub struct State {
name: String,
color: Color,
character: String,
}
创建一个以 Traits 作为值的全局可变 HashMap
lazy_static! {
static ref StateOK: Mutex<State> = {
let mut state = State{
name: String::from("OK"),
color: Color::Green,
character: "+".to_string()
};
Mutex::new(state)
};
static ref STATES: Mutex<HashMap<&'static str, &'static Lazy<Mutex<dyn _State + 'static>>>> = {
let mut _states = HashMap::from(
[
(
"OK",
Lazy::new(StateOK)
)
]
);
Mutex::new(_states)
};
}
所有源代码都可以在 github 上找到(yiet 不可编译):https://github.com/mauricelambert/TerminalMessages
上下文
我用的是外部库lazy_static.
假设
我想我应该使用 Mutex、Lazy 或其他允许我创建可变全局值的类型。
问题
我不知道如何定义我将在值中定义的对象的大小,这些对象可以是不同类型,其共同基础是函数签名。
- 我至少有 2 种类型的对象实现了 Trait 并且可以作为值添加:一种具有预配置颜色的 'simple' 类型和一种更复杂的类型,或者颜色由开发人员使用3 个八位字节(RGB - 红绿蓝)。
项目
我的代码的总体目的是用 Rust 实现一个 DLL,它具有其他语言的接口,允许在控制台中显示格式化和彩色消息。消息格式的每个元素都必须由开发人员“配置”(颜色、表示消息类型的字符、进度条等...)。它必须能够通过调用必须指定消息类型和消息内容的函数来使用这些消息类型中的任何一种。
Example/Demonstration
我在 Python 中实现了类似的代码,其源代码在 github: https://github.com/mauricelambert/PythonToolsKit/blob/main/PythonToolsKit/PrintF.py。
这是代表我想在此 DLL 中实现的内容的屏幕截图:!TerminalMessages demonstration
另外
我对所有关于 Rust 最佳实践和代码优化的建议感兴趣。
我认为您的代码中存在一些误解,imo:
- 要将特征对象存储在
HashMap
中,您需要将它们包装在 Box
中,因为正如您已经意识到的那样,特征对象不是 Sized
.
- 您不需要将对象本身包装在
Mutex
中,因为整个 HashMap
已经在 Mutex
. 中
考虑到这一点,这里有一个 working implementation:
use std::{collections::HashMap, sync::Mutex};
use lazy_static::lazy_static;
/// Preconfigured ANSI colors constants
#[derive(Clone)]
pub enum Color {
Black, // 0
Red, // 1
Green, // 2
Yellow, // 3
Blue, // 4
Purple, // 5
Cyan, // 6
White, // 7
}
impl Color {
fn value(&self) -> i32 {
match *self {
Color::Black => 0,
Color::Red => 1,
Color::Green => 2,
Color::Yellow => 3,
Color::Blue => 4,
Color::Purple => 5,
Color::Cyan => 6,
Color::White => 7,
}
}
}
/// _State line to define the basis of the 'State'
/// type with signatures of specific functions
pub trait _State {
fn as_string(&self, text: String) -> String;
fn print(&self);
}
/// Default state type using preconfigured ANSI colors
#[derive(Clone)]
pub struct State {
name: String,
color: Color,
character: String,
}
impl _State for State {
fn as_string(&self, text: String) -> String {
format!(
"\x1b[3{color}m[{character}] {text}\x1b[0m",
color = self.color.value(),
character = self.character,
text = text,
)
}
fn print(&self) {
println!("{}", self.as_string(self.name.clone()));
}
}
lazy_static! {
static ref STATE_OK: State = {
State {
name: String::from("OK"),
color: Color::Green,
character: "+".to_string(),
}
};
static ref STATES: Mutex<HashMap<&'static str, Box<dyn _State + Send>>> = {
let _states: HashMap<&'static str, Box<dyn _State + Send>> = HashMap::from([
("OK", Box::new(STATE_OK.clone()) as Box<dyn _State + Send>),
(
"NOK",
Box::new(State {
name: String::from("NOK"),
color: Color::Yellow,
character: "-".to_string(),
}) as Box<dyn _State + Send>,
),
]);
Mutex::new(_states)
};
}
fn main() {
println!("{}", STATES.lock().unwrap().len());
for (key, value) in &*STATES.lock().unwrap() {
println!("{}:", key);
value.print();
println!("");
}
}
进一步调整
这些都是我的意见,要么接受要么放弃。
- 使
trait _State
依赖于 Send
,因为它们都必须是 Send
才能存储在 HashMap
中。这使得 HashMap
定义更清晰一些
- 编写
state_entry
辅助函数来简化初始化
use std::{collections::HashMap, sync::Mutex};
use lazy_static::lazy_static;
/// Preconfigured ANSI colors constants
#[derive(Clone)]
pub enum Color {
Black, // 0
Red, // 1
Green, // 2
Yellow, // 3
Blue, // 4
Purple, // 5
Cyan, // 6
White, // 7
}
impl Color {
fn value(&self) -> i32 {
match *self {
Color::Black => 0,
Color::Red => 1,
Color::Green => 2,
Color::Yellow => 3,
Color::Blue => 4,
Color::Purple => 5,
Color::Cyan => 6,
Color::White => 7,
}
}
}
/// _State line to define the basis of the 'State'
/// type with signatures of specific functions
pub trait _State: Send {
fn as_string(&self, text: String) -> String;
fn print(&self);
}
/// Default state type using preconfigured ANSI colors
#[derive(Clone)]
pub struct State {
name: String,
color: Color,
character: String,
}
impl _State for State {
fn as_string(&self, text: String) -> String {
format!(
"\x1b[3{color}m[{character}] {text}\x1b[0m",
color = self.color.value(),
character = self.character,
text = text,
)
}
fn print(&self) {
println!("{}", self.as_string(self.name.clone()));
}
}
fn state_entry(
name: &'static str,
entry: impl _State + 'static,
) -> (&'static str, Box<dyn _State>) {
(name, Box::new(entry))
}
lazy_static! {
static ref STATE_OK: State = {
State {
name: String::from("OK"),
color: Color::Green,
character: "+".to_string(),
}
};
static ref STATES: Mutex<HashMap<&'static str, Box<dyn _State>>> = {
Mutex::new(HashMap::from([
state_entry("OK", STATE_OK.clone()),
state_entry(
"NOK",
State {
name: String::from("NOK"),
color: Color::Yellow,
character: "-".to_string(),
},
),
state_entry(
"ERROR",
State {
name: String::from("ERROR"),
color: Color::Red,
character: "!".to_string(),
},
),
]))
};
}
fn main() {
println!("{} entries\n", STATES.lock().unwrap().len());
for (key, value) in &*STATES.lock().unwrap() {
println!("{}:", key);
value.print();
println!("");
}
}
我正在尝试实现值键对,其键是字符串,值是实现某些预定义函数的对象类型。我遇到的问题是特征(我在这种情况下将其用作一种继承)没有预定义的大小并且不是线程安全的。
代码
创建特征和结构
/// _State line to define the basis of the 'State'
/// type with signatures of specific functions
pub trait _State {
fn as_string(&self, text: String) -> String;
fn print(&self);
}
/// Status type to add your custom colors
pub struct RGBState {
name: String,
color: (u8, u8, u8),
character: String,
}
/// Default state type using preconfigured ANSI colors
pub struct State {
name: String,
color: Color,
character: String,
}
创建一个以 Traits 作为值的全局可变 HashMap
lazy_static! {
static ref StateOK: Mutex<State> = {
let mut state = State{
name: String::from("OK"),
color: Color::Green,
character: "+".to_string()
};
Mutex::new(state)
};
static ref STATES: Mutex<HashMap<&'static str, &'static Lazy<Mutex<dyn _State + 'static>>>> = {
let mut _states = HashMap::from(
[
(
"OK",
Lazy::new(StateOK)
)
]
);
Mutex::new(_states)
};
}
所有源代码都可以在 github 上找到(yiet 不可编译):https://github.com/mauricelambert/TerminalMessages
上下文
我用的是外部库lazy_static.
假设
我想我应该使用 Mutex、Lazy 或其他允许我创建可变全局值的类型。
问题
我不知道如何定义我将在值中定义的对象的大小,这些对象可以是不同类型,其共同基础是函数签名。
- 我至少有 2 种类型的对象实现了 Trait 并且可以作为值添加:一种具有预配置颜色的 'simple' 类型和一种更复杂的类型,或者颜色由开发人员使用3 个八位字节(RGB - 红绿蓝)。
项目
我的代码的总体目的是用 Rust 实现一个 DLL,它具有其他语言的接口,允许在控制台中显示格式化和彩色消息。消息格式的每个元素都必须由开发人员“配置”(颜色、表示消息类型的字符、进度条等...)。它必须能够通过调用必须指定消息类型和消息内容的函数来使用这些消息类型中的任何一种。
Example/Demonstration
我在 Python 中实现了类似的代码,其源代码在 github: https://github.com/mauricelambert/PythonToolsKit/blob/main/PythonToolsKit/PrintF.py。 这是代表我想在此 DLL 中实现的内容的屏幕截图:!TerminalMessages demonstration
另外
我对所有关于 Rust 最佳实践和代码优化的建议感兴趣。
我认为您的代码中存在一些误解,imo:
- 要将特征对象存储在
HashMap
中,您需要将它们包装在Box
中,因为正如您已经意识到的那样,特征对象不是Sized
. - 您不需要将对象本身包装在
Mutex
中,因为整个HashMap
已经在Mutex
. 中
考虑到这一点,这里有一个 working implementation:
use std::{collections::HashMap, sync::Mutex};
use lazy_static::lazy_static;
/// Preconfigured ANSI colors constants
#[derive(Clone)]
pub enum Color {
Black, // 0
Red, // 1
Green, // 2
Yellow, // 3
Blue, // 4
Purple, // 5
Cyan, // 6
White, // 7
}
impl Color {
fn value(&self) -> i32 {
match *self {
Color::Black => 0,
Color::Red => 1,
Color::Green => 2,
Color::Yellow => 3,
Color::Blue => 4,
Color::Purple => 5,
Color::Cyan => 6,
Color::White => 7,
}
}
}
/// _State line to define the basis of the 'State'
/// type with signatures of specific functions
pub trait _State {
fn as_string(&self, text: String) -> String;
fn print(&self);
}
/// Default state type using preconfigured ANSI colors
#[derive(Clone)]
pub struct State {
name: String,
color: Color,
character: String,
}
impl _State for State {
fn as_string(&self, text: String) -> String {
format!(
"\x1b[3{color}m[{character}] {text}\x1b[0m",
color = self.color.value(),
character = self.character,
text = text,
)
}
fn print(&self) {
println!("{}", self.as_string(self.name.clone()));
}
}
lazy_static! {
static ref STATE_OK: State = {
State {
name: String::from("OK"),
color: Color::Green,
character: "+".to_string(),
}
};
static ref STATES: Mutex<HashMap<&'static str, Box<dyn _State + Send>>> = {
let _states: HashMap<&'static str, Box<dyn _State + Send>> = HashMap::from([
("OK", Box::new(STATE_OK.clone()) as Box<dyn _State + Send>),
(
"NOK",
Box::new(State {
name: String::from("NOK"),
color: Color::Yellow,
character: "-".to_string(),
}) as Box<dyn _State + Send>,
),
]);
Mutex::new(_states)
};
}
fn main() {
println!("{}", STATES.lock().unwrap().len());
for (key, value) in &*STATES.lock().unwrap() {
println!("{}:", key);
value.print();
println!("");
}
}
进一步调整
这些都是我的意见,要么接受要么放弃。
- 使
trait _State
依赖于Send
,因为它们都必须是Send
才能存储在HashMap
中。这使得HashMap
定义更清晰一些 - 编写
state_entry
辅助函数来简化初始化
use std::{collections::HashMap, sync::Mutex};
use lazy_static::lazy_static;
/// Preconfigured ANSI colors constants
#[derive(Clone)]
pub enum Color {
Black, // 0
Red, // 1
Green, // 2
Yellow, // 3
Blue, // 4
Purple, // 5
Cyan, // 6
White, // 7
}
impl Color {
fn value(&self) -> i32 {
match *self {
Color::Black => 0,
Color::Red => 1,
Color::Green => 2,
Color::Yellow => 3,
Color::Blue => 4,
Color::Purple => 5,
Color::Cyan => 6,
Color::White => 7,
}
}
}
/// _State line to define the basis of the 'State'
/// type with signatures of specific functions
pub trait _State: Send {
fn as_string(&self, text: String) -> String;
fn print(&self);
}
/// Default state type using preconfigured ANSI colors
#[derive(Clone)]
pub struct State {
name: String,
color: Color,
character: String,
}
impl _State for State {
fn as_string(&self, text: String) -> String {
format!(
"\x1b[3{color}m[{character}] {text}\x1b[0m",
color = self.color.value(),
character = self.character,
text = text,
)
}
fn print(&self) {
println!("{}", self.as_string(self.name.clone()));
}
}
fn state_entry(
name: &'static str,
entry: impl _State + 'static,
) -> (&'static str, Box<dyn _State>) {
(name, Box::new(entry))
}
lazy_static! {
static ref STATE_OK: State = {
State {
name: String::from("OK"),
color: Color::Green,
character: "+".to_string(),
}
};
static ref STATES: Mutex<HashMap<&'static str, Box<dyn _State>>> = {
Mutex::new(HashMap::from([
state_entry("OK", STATE_OK.clone()),
state_entry(
"NOK",
State {
name: String::from("NOK"),
color: Color::Yellow,
character: "-".to_string(),
},
),
state_entry(
"ERROR",
State {
name: String::from("ERROR"),
color: Color::Red,
character: "!".to_string(),
},
),
]))
};
}
fn main() {
println!("{} entries\n", STATES.lock().unwrap().len());
for (key, value) in &*STATES.lock().unwrap() {
println!("{}:", key);
value.print();
println!("");
}
}