Rust枚举中str / String值的最佳实践是什么?

时间:2017-05-18 17:51:31

标签: string enums rust lifetime

我有这个真的很令人愉快,但我很担心它的含义:

#[derive(Eq, PartialEq, Debug)]
pub enum SmtpHost {     
    DOMAIN(String),
    IPV4(Ipv4Addr),
    IPV6(Ipv6Addr),
    UNKNOWN { label:String, literal:String },
}

我正在用PEG语法填充这个,这给我&str所以所有的字符串调用都是这样的 - SmtpHost::Domain(s.to_string())

我希望这些枚举成为解析器的结果,例如smtp_parser::host< 'input >(s: 'input & str) -> SmtpHost

我也尝试过ref方法,但很快就开始变得笨拙了:

#[derive(Eq, PartialEq, Debug)]
pub enum SmtpHost<'a > {     
    DOMAIN(&'a str),
    IPV4(Ipv4Addr),
    IPV6(Ipv6Addr),
    UNKNOWN { label:&'a str, literal:&'a str },
}

所以我喜欢或者......但是你知道的更好。告诉我:o)

我的study project for reference

2 个答案:

答案 0 :(得分:1)

如果要在不复制的情况下进行解析, 那么你想要的签名是:

// Notice that the 'input goes after the &. Syntax.
fn smtp_parser::host<'input>(s: &'input str) -> SmtpHost<'input>;

然后你可以这样定义你的枚举:

#[derive(Eq, PartialEq, Debug)]
pub enum SmtpHost<'input> {
    DOMAIN(&'input str),
    IPV4(Ipv4Addr),
    IPV6(Ipv6Addr),
    UNKNOWN { label: &'input str, literal: &'input str },
}

另一方面,如果在某些情况下这太尴尬了,你可以使用Cow(写时复制)类型两者

use std::borrow::Cow;
#[derive(Eq, PartialEq, Debug)]
pub enum SmtpHost<'input> {
    DOMAIN(Cow<'input, str>),
    IPV4(Ipv4Addr),
    IPV6(Ipv6Addr),
    UNKNOWN { label: Cow<'input, str>, literal: Cow<'input, str> },
}

如果主机部件有时可以直接从输入中使用,那么这就是你想要做的,但有时需要在它可用之前进行更改。

答案 1 :(得分:1)

&strString之间的重要区别在于所有权。 String是拥有的,但&str是借来的。如果存储&str值,则容器的生命周期将限制为借用字符串的生命周期。

如果您的解析器生成器生成一个带有如下签名的解析函数:

smtp_parser::host<'a>(&'a str) -> SmtpHost<'a>

然后当它传递给你一个&str供你用来构造你的解析树/解析值时,它很可能会给你一个输入的子串。这意味着您在&str枚举中存储的SmtpHost的生命周期必须短于原始输入字符串。事实上,你可以在签名中看到这一点;输入字符串和输出SmtpHost都有生命周期参数'a

这意味着您生成的SmtpHost不能比用于生成它的输入寿命更长。如果输入是字符串常量&'static str,那可能没问题,但是如果你从标准输入或读取文件中获得输入,你将无法返回SmtpHost超过该点输入字符串所在的位置。

例如,假设您要声明一个从标准中解析SmtpHost的函数:

fn read_host<'a>() -> SmtpHost<'a> {
    let mut line = String::new();
    let stdin = io::stdin();
    stdin.lock().read_line(&mut line).expect("Could not read line");
    smtp_parser::host(&line)
}

你会收到一个错误,上面写着“行不够长”的错误。 Here's a trivial example in Rust playground

因此,当您只是从其他地方借用一个不需要超过源的值时,您应该使用&str。当您需要拥有该值时,您应该使用String

对于更复杂的情况,您需要拥有自有值,但希望能够在多个地方使用它而不需要多个副本,因此Rc<T>Rc<RefCell<T>。但在你的情况下,听起来SmtpHost应该拥有它存储的字符串的所有权。