[英]Creating recursive enum -- should I use lifetime references? (Rust)
I want to create an ADT like:我想创建一个 ADT,如:
pub enum Predicate<'a>{
And(Box<&'a Predicate>, Box<&'a Predicate>),
Neg(Box<&'a Predicate>),
Bool(LogicOp, Literal, Literal)
}
But apparently this is not the correct way to specify lifetimes.但显然这不是指定生命周期的正确方法。
My question is two-fold:我的问题有两个:
let foo = Predicate::Bool(whatever args);
let and = Predicate::And(&foo, &foo);
let neg = Predicate::Neg(&and);
let bar = Predicate::And(&and, &neg);
and so forth and continue to be able to use foo
, and
, and neg
later on.依此类推,以后可以继续使用
foo
、 and
和neg
。
If everything really is immutable, then the simplest way to handle this scenario is with Rc
/ Arc
(the only difference between the two is that the latter can be used for objects accessed from multiple threads.) You can define your enum as follows:如果一切真的是不可变的,那么处理这种情况的最简单方法是使用
Rc
/ Arc
(两者之间的唯一区别是后者可用于从多个线程访问的对象。)您可以定义枚举如下:
pub enum Predicate {
And(Rc<Predicate>, Rc<Predicate>),
Neg(Rc<Predicate>),
Bool(LogicOp, Literal, Literal)
}
An Rc
is a reference-counted shared pointer to a heap-allocated object. Rc
是一个引用计数的共享指针,指向堆分配的 object。 You can clone it to obtain a new pointer to the internal data without any copying, and you will never have to worry about lifetimes: internally, the Rc
keeps track of how many references are being held to its object, and automatically deallocates it when this number drops to zero.您可以克隆它以获得指向内部数据的新指针而无需任何复制,并且您永远不必担心生命周期:在内部,
Rc
会跟踪对其 object 的引用数量,并在此情况下自动释放它数字下降到零。 This bookkeeping incurs a small runtime cost whenever the Rc
is cloned or dropped, but greatly simplifies dealing with this sort of shared ownership.每当克隆或删除
Rc
时,这种簿记会产生很小的运行时成本,但极大地简化了处理这种共享所有权的过程。
Of course, the use of an Rc
makes it somewhat more verbose to create predicates.当然,使用
Rc
使得创建谓词更加冗长。 The example you give would become:您给出的示例将变为:
let foo = Rc::new(Predicate::Bool(whatever args));
let and = Rc::new(Predicate::And(foo.clone(), foo.clone()));
let neg = Rc::new(Predicate::Neg(and.clone()));
let bar = Rc::new(Predicate::And(and.clone(), neg.clone()));
(Technically not all these clones are necessary if you did not intend to use, say, neg
later.) (从技术上讲,如果您以后不打算使用,例如,
neg
,则并非所有这些克隆都是必需的。)
One way to ease this boilerplate is to use methods or associated functions on the Predicate
type to create pre- Rc
ed values, like so:简化此样板文件的一种方法是使用
Predicate
类型上的方法或相关函数来创建预Rc
ed 值,如下所示:
impl Predicate {
pub fn and(a: &Rc<Predicate>, b: &Rc<Predicate>) -> Rc<Predicate> {
Rc::new(Predicate::And(a.clone(), b.clone())
}
pub fn neg(a: &Rc<Predicate>) -> Rc<Predicate> {
Rc::new(Predicate::Neg(a.clone()))
}
pub fn boolean(op: LogicOp, a: Literal, b: Literal) -> Rc<Predicate> {
Rc::new(Predicate::Bool(op, a, b))
}
}
With this, your example becomes:有了这个,你的例子变成:
let foo = Predicate::boolean(whatever args);
let and = Predicate::and(&foo, &foo);
let neg = Predicate::neg(&and);
let bar = Predicate::and(&and, &neg);
There is one unavoidable downside to this approach, though.但是,这种方法有一个不可避免的缺点。 You cannot
match
through an Rc
without dereferencing it first, which can make working with Rc
ADTs somewhat painful.如果不先取消引用,就无法通过
Rc
进行match
,这会使使用Rc
ADT 变得有些痛苦。 See this question for details.有关详细信息,请参阅此问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.