惯用 Rust:字符串标记化应该是函数、特殊特征还是 TryFrom 特征?

Idiomatic Rust: should string tokenization be a function, a special trait or a TryFrom trait?

我在空格处拆分了一行文本,并希望将所有类似 ALALEUVAL 等形式的片段转换为 Token 类型。匹配很简单,但我是 rust 的新手,我不确定如何推理实现转换本身。

我将它实现为 StringTryFrom 特征(见下文),因为对于初始文本中的大多数字符串片段,转换将失败,所以我认为这是一个很好的方法有过滤机制。我应该宁愿使用独立功能吗?我最终如何为 &str 实现这个(这次我因为借用规则失败了)?


#[derive(Debug)]
enum AminoAcid {
    VAL,    GLN,    ARG,    LEU,
    THR,    TYR,    SER,    PRO,
    CYS,    GLY,    ALA,    MET,
}
#[derive(Debug)]
struct Coordinate {
    x: f32,    y: f32,    z: f32,
}
#[derive(Debug)]
enum Token {
    Coordinate(Coordinate),
    AminoAcid(AminoAcid),
}

fn match_string(tgt: &str) -> Option<Token> {
    match tgt {
        "VAL" => Some(AminoAcid::VAL.into()),
        ..., // etc.
        "ALA" => Some(AminoAcid::ALA.into()),
        "MET" => Some(AminoAcid::MET.into()),
        _ => None,
    }


impl TryFrom<String> for Token {
    type Error = ();
    fn try_from(value: String) -> Result<Self, Self::Error> {
        let x = match match_string(&value) {
            Some(uu) => Ok(uu),
            None => Err(()),
        };
        x
    }
}

fn process_line(line: &str) -> Vec<Token> {
    let tokens = line.split_whitespace().collect::<Vec<&str>>();
    let mut results: Vec<Token> = vec![];
    for template_string in tokens {
        match template_string.to_string().try_into() {
            Ok(token) => {
                println!("Parsed to Token : {:?}", token);
                results.push(token)
            }
            _ => continue,
        };
    }
    return results;
}

我再次对有经验的 Rust 用户会想到的权衡取舍感兴趣。谢谢。

有很多方法可以实现这一点,但不建议将 FromStr 作为一种从字符串中解析值的惯用方法是一种伤害:

use std::str::FromStr;

#[derive(Debug)]
enum AminoAcid {
    VAL,    GLN,    ARG,    LEU,
    THR,    TYR,    SER,    PRO,
    CYS,    GLY,    ALA,    MET,
}

struct AminoAcidParseError;

impl FromStr for AminoAcid {
    type Err = AminoAcidParseError;
    fn from_str(s: &str) -> Result<AminoAcid, AminoAcidParseError> {
        match s {
            "VAL" => Ok(AminoAcid::VAL),
            // ... others
            "ALA" => Ok(AminoAcid::ALA),
            "MET" => Ok(AminoAcid::MET),
            _ => Err(AminoAcidParseError),
        }
    }
}

fn process_line(line: &str) {
    for s in line.split_whitespace() {
        match s.parse::<AminoAcid>() {
            Ok(a) => println!("Parsed to AminoAcid : {:?}", a),
            Err(_) => {},
        }
    }
}

请参阅 playground 上的完整示例。

实现这个特性允许你在任何 str 上调用 .parse() 来错误地获取值。我只针对您的 AminoAcid 类型介绍了它,但您可以了解如何针对 Token 及以后的类型进行操作。