Как мне преобразовать перечисления MySQL в перечисления Rust при использовании mysql crate?

#mysql #rust

#mysql #Ржавчина

Вопрос:

Я хочу прочитать перечисления из таблицы MySQL, но мне не удается преобразовать строковые перечисления из таблицы в реальные перечисления Rust.

Какие варианты у меня есть? В документации говорится, что я должен реализовать FromValue функцию:

Cargo.toml

 [dependencies]
mysql = "15.1.0"
strum = "0.14.0"
strum_macros = "0.14.0"
  
 use mysql::{
    prelude::{ConvIr, FromValue},
    Value,
};
use std::str::{from_utf8, FromStr};
use strum_macros::{AsStaticStr, EnumString};

#[derive(Debug, PartialEq, Eq, EnumString, AsStaticStr)]
pub enum UserRole {
    ADMIN,
    USER,
}

#[derive(Debug)]
pub struct EnumIr {
    bytes: Vec<u8>,
}

impl ConvIr<UserRole> for EnumIr {
    fn new(v: Value) -> mysql::error::Result<EnumIr> {
        match v {
            Value::Bytes(bytes) => match from_utf8(amp;*bytes) {
                Ok(_) => Ok(EnumIr { bytes: bytes }),
                Err(_) => Err(mysql::FromValueError(Value::Bytes(bytes))),
            },
            v => Err(mysql::FromValueError(v)),
        }
    }
    fn commit(self) -> UserRole {
        unsafe { UserRole::from_str(from_utf8(amp;self.bytes).unwrap()).unwrap() }
    }
    fn rollback(self) -> Value {
        Value::Bytes(self.bytes)
    }
}

impl FromValue for UserRole {
    type Intermediate = EnumIr;
}

fn main() {
    println!("Hello, world!");
}
  

Это завершается ошибкой со следующим сообщением об ошибке:

 error[E0053]: method `new` has an incompatible type for trait
  --> src/main.rs:20:5
   |
20 |     fn new(v: Value) -> mysql::error::Result<EnumIr> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `mysql_common::value::convert::FromValueError`, found enum `mysql::error::Error`
   |
   = note: expected type `fn(mysql_common::value::Value) -> std::result::Result<EnumIr, mysql_common::value::convert::FromValueError>`
              found type `fn(mysql_common::value::Value) -> std::result::Result<EnumIr, mysql::error::Error>`
  

Кому-нибудь удалось выполнить преобразование или есть подход получше?

Комментарии:

1. Спасибо за ответ! К сожалению, rust playground не включает mysql crate, поэтому я не могу привести пример. Моя проблема в том, что fn new(v: Value) -> mysql::Result<EnumIr> я получаю expected struct mysql_common::value::convert::FromValueError, found enum mysql::error::Error , что я пытался вернуть mysql:: FromValueError и mysql::error::FromValueError, но они оба не работают. Однако структура mysql_common::value_convert::FromValueError является частной.

2. Извините, это мой первый вопрос, спасибо, что уделили мне время. Приведенный выше код теперь взят из нового проекта cargo. Вы должны иметь возможность скопировать и запустить его, чтобы воспроизвести ошибки компилятора.

3. Процесс, который вы только что прошли, является правильным процессом для того, чтобы задать вопрос в любом контексте; в Stack Overflow, в системе отслеживания проблем, в чате или даже лично; имейте это в виду, и вы получите гораздо лучшие ответы (а также, скорее всего, будете чаще отвечать на свой собственный вопрос).

4. Можете ли вы объяснить, чего вы не понимаете в ошибке компилятора? Здесь говорится, что вам нужно вернуть FromValueError но вы возвращаете mysql::error::Error ; почему вы используете неправильный тип?

5. Почему вы ссылаетесь на документацию для версии 1.2.0 , когда используемая вами версия — 15.1.0 ?

Ответ №1:

Как указано в сообщении об ошибке (немного очищено):

 expected type `fn(Value) -> Result<EnumIr, FromValueError>`
   found type `fn(Value) -> Result<EnumIr, mysql::Error>`
  

Вы не можете вернуть неправильный тип; это просто фундаментальное условие статически типизированного языка. Вместо этого верните правильный тип: Result<EnumIr, mysql::FromValueError> .

Я также изменил ваш код, чтобы избежать ложного повторного разбора данных в строку:

 #[derive(Debug)]
pub struct EnumIr {
    string: String,
}

impl ConvIr<UserRole> for EnumIr {
    fn new(v: Value) -> Result<EnumIr, mysql::FromValueError> {
        match v {
            Value::Bytes(bytes) => match String::from_utf8(bytes) {
                Ok(string) => Ok(EnumIr { string }),
                Err(e) => Err(mysql::FromValueError(Value::Bytes(e.into_bytes()))),
            },
            v => Err(mysql::FromValueError(v)),
        }
    }

    fn commit(self) -> UserRole {
        self.string.parse().unwrap()
    }

    fn rollback(self) -> Value {
        Value::Bytes(self.string.into_bytes())
    }
}
  

Также кажется, что вам следует попытаться проанализировать перечисление в new методе, поскольку это единственный, который допускает Result :

 #[derive(Debug)]
pub struct EnumIr {
    role: UserRole,
    string: String,
}

impl ConvIr<UserRole> for EnumIr {
    fn new(v: Value) -> Result<EnumIr, mysql::FromValueError> {
        match v {
            Value::Bytes(bytes) => match String::from_utf8(bytes) {
                Ok(string) => match string.parse() {
                    Ok(role) => Ok(EnumIr { role, string }),
                    Err(_) => Err(mysql::FromValueError(Value::Bytes(string.into_bytes()))),
                },
                Err(e) => Err(mysql::FromValueError(Value::Bytes(e.into_bytes()))),
            },
            v => Err(mysql::FromValueError(v)),
        }
    }

    fn commit(self) -> UserRole {
        self.role
    }

    fn rollback(self) -> Value {
        Value::Bytes(self.string.into_bytes())
    }
}
  

Комментарии:

1. Большое вам спасибо! Я только что проверил в своем более крупном проекте и да: ваш код правильно преобразует значение строки mysql в перечисления rust, так здорово!