簡體   English   中英

如何以 str 為鍵、Trait 為值實現一個全局且可變的 HashMap?

[英]How to implement a global and mutable HashMap with str as key and Trait as values?

我正在嘗試實現值鍵對,其鍵是字符串,值是實現一些預定義函數的對象類型。 我遇到的問題是 Trait(在這種情況下我將其用作一種繼承)沒有預定義的大小並且不是安全的線程。

代碼

創建特征和結構

/// _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 上提供(不可編譯 yet): https ://github.com/mauricelambert/TerminalMessages

語境

我使用外部庫lazy_static

假設

我想我應該使用 Mutex、Lazy 或其他允許我創建可變和全局值的類型。

問題

我不知道如何定義我將在值中定義的對象的大小,這些對象可以是不同類型,其共同基礎是函數簽名。

  • 我至少有 2 種類型的對象實現 Trait 並且可以作為值添加:具有預配置顏色的“簡單”類型和更復雜的類型,或者顏色由開發人員使用 3 個八位字節(RGB - 紅綠藍色的)。

項目

我的代碼的總體目的是在 Rust 中實現一個 DLL,該 DLL 具有其他語言的接口,允許在控制台中顯示格式化和彩色消息。 消息格式的每個元素都必須由開發人員“配置”(顏色、表示消息類型的字符、進度條等......)。 它必須能夠通過調用必須指定消息類型和消息內容的函數來使用這些消息類型中的任何一種。

示例/演示

我在 Python 中實現了類似的代碼,其源代碼在 github 上: https ://github.com/mauricelambert/PythonToolsKit/blob/main/PythonToolsKit/PrintF.py。 這是一個屏幕截圖,代表我想在這個 DLL 中實現的內容:! TerminalMessages 演示

此外

我對所有關於 Rust 和代碼優化最佳實踐的建議感興趣。

我認為您的代碼中存在一些誤解,imo:

  • 要將特征對象存儲在HashMap中,您需要將它們包裝在Box中,因為正如您已經意識到的那樣,特征對象不是Sized
  • 您不需要將對象本身包裝在Mutex中,因為整個HashMap已經在Mutex中。

考慮到這一點,這是一個有效的實現

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!("");
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM