On 11/5/13 2:44 AM, spir wrote:
Why not just add a declaration of the trait at the top of the struct
type def?
struct PairList<Val> : Iterable {
You can implement traits on types that aren't structs.
* either the methods inside the struct def:
struct PairList<Val> {
fn push (&self, key:Key, val:Val) {...}
}
That seems somewhat ad-hoc IMHO, because you can implement traits on
types that aren't structs.
* or a simple form outside the struct def:
fn PairList.push (&self, key:Key, val:Val) {...}
It is stated somewhere that the impl section nicely separates the fields
from the implementation, but this is a question of taste: one could
trivially reply this section "invents" a strange notion of
implementation (after all, data fields also are implementation, or
rather also definition), and forces to tell apart things that logically
fit together. Anyway, it's a question of perspective... I'd vote for the
second form above, because it is a kind of intermediate choice. Another
advantage is it gives a name to the method ("PairList.push").
We considered the second form, but rejected it, because if you had
generic type parameters you'd have to repeat them over and over.
Also, I'm not clear about whether "impl for trait" sections form a kind
of namespace. Can there be methods with equal names in diverse such
sections (and/or in in th impl section or section not related to
traits)? If yes, then it would be in my view a bad idea. Instead, make a
kind of standard naming scheme for trait methods.
Else, what do such sections mean?
I'm not sure what this means.
As shown above, for a struct type with type params, we have to repeat
the said type params in the impl section's headline, and this in fact
twice! (I had a hard time with that point.) These repetitions make no
sense, in my view, since the type param belongs to the struct type
anyway, esp the one after "impl"; but this one repetition precisely is
where to declare traits on type params... see below. I could live with
the repetition after the struct type name, as if the type param belonged
to the name of the struct type:
impl PairList<Val> {
(but as said I would happily forget about impl sections altogether)
There's a big difference between:
impl<T> SomeTrait for Foo<T> { ... }
And:
impl SomeTrait for Foo<int> { ... }
We want to be able to tell the difference between the two at parse time.
There is certainly a way to use traits in an opposite manner: they
represent functionalities of which we may know they are available when
we use other features or types of this or that kind. Currently, traits
act conversely, as a constant barrier to usage and expression. With
traits as they are (used), it looks like everything is forbidden by
default, and we have to discover and explicitely authorise any single
bit of functionality.
Rust doesn't have C++-style ad-hoc templates; instead it requires that
you use concepts in all circumstances. There are two reasons for this:
(a) to fix the error message problems of C++ templates; (b) to allow
functionality to be added to existing types and used in templates
without the C++ *argument-dependent lookup*. Given that C++ is heavily
moving toward concepts because the error message problems at least seem
intractable without them, I'm personally pretty happy with the decision
that we made. It requires more organization up front, but the user
experience of the language becomes so much nicer.
I love it that a range of "prelude" features are available in standard.
A tiny improvement would be that we don't even need to prefix everything
with "std::". (In the code above, I had to declare "use std:fmt").
Instead, just as every prelude feature is available as if we had
explicitely imported it, make every (sub)module of std pretend to be at
the root of the library. Meaning, whenever the language does not find
x::y, have it search std:x:y.
This would be particularly appreciated for traits which, as it seems,
we'll have to deal with *very* much.
We used to do this, but we stopped. The reasons have to do with making
imports tractable and the fact that we didn't want to wire "std" into
the compiler more than it needs to be.
Now, have a look at [http://static.rust-lang.org/doc/0.8/std/vec.html]:
it is by me a webpage long of 50 screens (I use rather big fonts,
right); I did not count the functions, but there may be more than 200
probably. And this is nearly a plain list, most funcs are undocumented
or just with a short sentence. Actually, most of them seem redondant,
there are tons of versions of the same features, depending on various
variants.
The `vec` module does need to be cleaned up, agreed.
I guess much of this mess is due to a kind of over-engineering
related in part to traits, or to their current usage in Rust. It seems
the combination of traits, and their combinations with other sources of
complication such as type params, add barriers to usage and expression.
I would caution against drawing too much of a conclusion from the `vec`
module. Most of those traits exist just so that the standard library can
add methods to the primitive vector type. Remember that in most other
languages you can't add methods to primitive types at all.
If vectors weren't a built-in type, then you wouldn't have to use traits
to define all these methods. That would be ideal if we had enough time
to fix it before 1.0, but regrettably I don't think it'll happen. (We
may be able to sneak it in in 2.0 backwards compatibly, however.)
Ultimately I think you want ad-hoc templates like C++/D/Nimrod. I don't
see that happening, unfortunately: Rust is fairly firmly in the Haskell
tradition of typeclasses for abstraction at this point.
Patrick
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev