Reviving an old thread here because I'm still working on this.
I suspect you'll want to make a trait `Spanned` that the various
spanned-types implement, such that code that generically acts on "Some
spanned thing" can continue to work. Not sure, just a hunch.
I thought so too, but after removing spanned<T> from a few types I have
not encountered the need to do so so far. The reason is probably that
without traits a common abstraction over 'everthing having a span'
couldn't be expressed, so no code was written that would rely upon such
an abstraction.
(spanned<T> predates having traits at all, so if there's a better way to
approach this in current idioms, go right ahead)
It turns out that my initial reasoning for getting rid of spanned<T>
didn't take enums properly into account. For these, having a wrapper
containing data common to all variants makes more sense than for
structs. Because otherwise the common fields have to be put in every
variants:
struct spanned<T> {
span: span,
node: T
}
// This might be more convenient to use...
pub type decl = spanned<decl_>;
pub enum decl_ {
decl_local(@Local),
decl_item(@item),
}
// .. than this:
pub enum decl {
decl_local(@Local, span),
decl_item(@item, span),
}
Especially when the enum an has many variants (such as `ast::expr_` with
33 variants) adding a `span` field to all of them is kind of annoying.
Apart from spanned<T> there are quite a few such wrapper structs
containing common fields: ast::expr, ast::pat, ast::foreign_item,
ast::item, ast::view_item, and ast::Ty to name only those in
libsyntax/ast.rs
To me this seems very much like a slightly clunky way of emulating
inheritance (with a lot more typing involved at definition and usage
sites). Field accessor functions or traits can alleviate some of this
clunkyness at the usage site; however, at the cost of even more
boilerplate needed at the definition site:
pub enum decl {
decl_local(@Local, span),
decl_item(@item, span),
}
impl decl {
fn get_span(&self) {
// this match statement also introduces some unnecessary
runtime overhead...
match *self {
decl_local(_, span) => span,
decl_item(_, span) => span
}
}
}
This situation is not very satisfactory but I don't know an easy
solution to the problem. I certainly don't suggest adding such a heavy
tool as inheritance to Rust just for this use case.
So for now I'll keep adapting things to the new naming convention but
I'll leave wrapped enums as they are. I'm very much interested in any
comments regarding this problem of types with some common fields and
some specialized fields. Type specialization is something popping up all
the time and Rust seems to have some weak spots there.
Some concrete style questions:
- Do enum variants follow the guideline for variable_naming or
TypeNaming. (Although, imo enum variants should be *subtypes* of their
defining enum anyway ;)
- What is the preferred naming convention for getter functions such as
`get_span()` above? get_span()? just span()? something else?
Thanks for reading!
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev