我有这个真的很令人愉快,但我很担心它的含义:
#[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)
答案 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)
&str
和String
之间的重要区别在于所有权。 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
应该拥有它存储的字符串的所有权。