Hello,
New to Rust. Is there (already) a list for mutual help in the usage of Rust? If
not, I guess it may be worth having one, distinct from the dev list, even if the
language is a moving target, even for people who rather intend, at terms, to
participate in the development. It is in fact even more needed due precisely to
the constant change of the lang, to the fact it is mostly unknown, and to the
state of the docs.
In the meanwhile... see below. Also please pardon wrong ideas or statements: I
am presently discovering the language. (Also, english is a foreign lang for me.)
As an exercise in learning the language, I am trying various ways to implement
sparse arrays (association list, trie, "modulo table" = hash table w/o hash),
the aim being to determine the nr of entries from which it is worth switching to
a more complicated version. But I'm far from there yet, stumbling on diverse
issues right for the simplest bits of code. Below, I'm talking of the
interrelation between struct type declaration & impl, type params, and traits.
type Key = uint;
struct PairList<Val> {
...
}
impl<Val:fmt::Default> PairList<Val> {
...
}
impl<Val> PairList<Val> for Iterable {
...
}
=== trait implementation ===
A first point which I find annoying is the splitting of impl for traits a struct
explicitely implements. This means that each time we need to use a generic func
for the *usage* of an *instance* of a type, we have to modify this type's
*definition* with a new impl section for the corresponding trait(s) required by
the generic feature. Or do I misunderstand? (This, even if the the trait is
actually implemented, I mean the methods exist, or am I wrong again?) I find
that exagerated.
Why not just add a declaration of the trait at the top of the struct type def?
struct PairList<Val> : Iterable {
=== impl itself ===
As a side note, I also do not understand the purpose of the impl section
altogether. I would be happy with:
* either the methods inside the struct def:
struct PairList<Val> {
fn push (&self, key:Key, val:Val) {...}
}
* 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").
=== the meaning of impl for trait sections ===
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?
=== impl & type params ===
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)
=== traits used ===
For programmer feedback (read: debug) I need to write out pair-lists (indeed).
An issue is that the Val type is a parameter. Since any form of structured
output itself requires a format (if only '?') and such formats are traits (!
why?), I need to declare this trait ("fmt::Default") as belonging to Val --yes,
the type param... But where? I had to search for a while before stepping on the
right place: as written above right after "impl" itself.
The logical place to make this declaration would be the concerned method, here
'write'. But the said method does not take any Val instance as input, just self:
so that there is no place there to declare that Val implements fmt::Default. Or
how are we to do it?
=== universal traits ===
A distinct but related issue is this trait fmt::Default is supposed universal.
Could type params have those universal traits? so that we don't have to declare
them. Or better, they are not traits.
=== everything is a trait ===
Apparently, in the latest dev of Rust, about everything becomes a trait. This
gets crazy ;-). I guess it is wrong in that it over-complicates the language,
and there is no limit. Moreover, these layers of complication *compose*, since
traits formally are independant. But they are not, semantically, I mean in the
proper sense of the term: what they *mean* (or are supposed to).
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.
See also "over-abstraction" below.
=== std features, esp traits ===
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.
=== over-abstraction ===
Traits look like, finally, de facto making ("inventing") sub-categories of
types. There are already many such categories in principle, eg for vectors
fix-size/var-size/dyn-size, mutable/immutable, fix-type/param-type, etc... Each
new trait or range of related traits (eg how they are to be written or iterated)
forms new sub-categories or panels of sub-categories. All of them in principle
are "orthogonal" (in the sense of logically independant).
Features like generics, type params, traits, should be an opportunity for
otherwise hyper-rigid languages, because statically typed, and like Rust aiming
at full safety, to regain some flexibility and ease of use, while retaining
their safety and efficiency. I guess this is what makes so-called dyn languages
easy to use. In such a language there is one type of sequential container, and 0
sub-category.
The features required to use such a type are at most a dozen methods plus a
handful of operators (python) or even less, in fact about nothing (lua). Lua may
arguably be extreme, but in Python this is enough to cover 99.9% of use cases,
other being trivially wri tten in a few lines of code in terms of existing features.
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. 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.
Instead of gaining flexibility and ease of use, we get the opposite as it seems.
We should just declare traits once and for all in the headline ot type defs,
this just for doc and to help the compiler, and then corresponding functionality
be available, also composing with functionality provided by other traits, and
automatically declining into subtypes due to genericity, etc...; this, without
any other declaration for other types, type params, and more, even less for each
method of the type or at each function using instances of the type. I guess
there is a way for traits to be door-openers, not barricade-fabricators. Or am I
dreaming?
I love many things in Rust, but am afraid it ends up proving being to difficult
to use or even learn. I'm pretty sure its core design, with some adjustments
maybe, permits driving it toward an other, welcoming direction. I'm searching
for a while already for a modern, flexible C with basic safety and reasonable
performance, a language fun and exciting to program with despite the difficulty
of lower-level coding, and think it could be Rust.
In any case, thank you very much.
Denis
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev