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

Reply via email to