I think I'm still missing something.
Even if I can deconstruct my Cow
s and pass them around, this is still very, very painful to do. Let's say I have two structures:
pub struct Block {
stmts: Vec<Stmt>,
ret: Expr,
}
pub enum Stmt {
While(Expr, Block),
Assign(Var, Expr),
}
and a similar enum for expressions. If I want to define a substitution function that operates on the AST, but only clones when necessary, I guess I have to do this using Cow
s.
What I would like to do intuitively is:
pub fn subst<'a>(self: Cow<'a, Self>, x: Var, y: Expr) -> Cow<'a, Self> {
match self {
Stmt::While(cond/*: Cow<'a, Expr>*/, b/*: Cow<'a, Block>*/) => Cow::Owned(cond.subst(x, y)?, b.subst(x, y)?),
/* */
}
}
And the same for structures: if self: Cow<'a, Block>
then self.ret: Cow<'a, Expr>
. I can't see the reason why it wouldn't be possible to have such things. Implementing it myself would probably need some smart use of Try
, because in the subst
function, we map to the identity if there is no substitution made in any of the lower nodes, otherwise we create a new item. Without Try or proper handling in match
, it would be really tedious I think, needing first to match on self
to know if I'm using a borrow or have ownership, and then for each branch, compute the subsitutions, and if any return a new owned element, compute a new owned element, otherwise return the reference wrapped in Cow::Borrowed