Re-CCing list because you don't seem to have dropped it intentionally and I'd prefer more experienced people to shout at me if I say something wrong.
On Tue, Jul 15, 2014 at 5:25 AM, Christoph Husse <[email protected]> wrote: > > > you're thinking in one paradigm exclusively while using in a multi-paradigm > > Well, it's really hard to see how to do it otherwise. I think maybe > the tutorial just needs to make it more clear and keep in mind that > most people come from a C#, C+ or Java background. It's nice to > present the "Rust" way but that is not enough to make it clear. > Somewhere there needs to be an obvious presentation of how to make the > transition from the "normal" patterns to the "rust" patterns. You're right, this is yet another demographic that would benefit from custom tutorials. However, I also believe many people can successfully pick up the general style just by reading "generic" material. Whether they'll be sold on it is another question... > > Second, the signature of MyClass::new() in your example is... unusual. > > Yes I noticed that too :D. But up till now I haven't looked into the > different pointer & reference types yet, that will be next on my list. > Just what I noticed is that I can't use trait implementations on the > struct object. I seem to NEED to cast the struct into a trait to use > the traits method (in contrast to what C# extension methods allow). > Is that a limitation of Rust right now or a design choice? Why doesn't > it know all the interfaces a trait/struct implements somewhere and > then let's you use all that without casting? I could imagine that this > would make things a lot easier for users. After all I can safely cast > it anyway, so the compiler could also do it behind the scenes without > exposing it to the user? This has nothing to do with pointers/references and everything to do with the trait system. You can (and probably already did, there's a couple in the prelude) use trait methods on concrete types. But the trait needs to be in scope (imported) for its methods to be available. Lacking time and technical writing skills to give a full tour, let me just sketch the reasons: Traits are not just Java interfaces, in particular they can be added almost anywhere and at any time (even in a completely different crate), so not only do they sometimes clash with each other (and importing one is a way to disambiguate) it's also pretty hard to make a comprehensive list of all trait impls. It's probably technically feasible to automagically import the impls that are in the same crate but this is undesirable for other reasons: It's surprising, can still lead to name collisions and pollutes namespaces in the same way glob imports do. It also makes less sense when one takes a less OOP-y interpretation of traits (e.g. as type classes) because many of the traits implemented for one type may not be interesting for most users. For example, if MyClass implements a serialization trait that the web framework uses internally, then none of my code needs that trait or its methods. Pulling them in adds no value and has the aforementioned downsides, especially if the web framework chose a method name you'd like to use for your actual business logic (cf. explicit interface implementations in C#). Keeping that trait out of my way makes life easier for me and for the framework authors, they don't need to choose stupid non-clashing names for their internals and I don't need to disambiguate (or worse, rename *my* methods). At first glance this appears to lead to a cascade of additional imports. This doesn't appear to be the case for all Rust code I've seen so far, not even the trait-heavy of your examples. First, usually many methods are defined in "impl MyStruct" blocks (those *are* always imported alongside MyStruct), or you'd want to import the trait anyway: Either to specify it as bound in a generic (fn foo<T: Trait>(x: T)) or to speak about trait objects (fn foo(x: &Trait)). In my limited experience, there's one trait I recall importing only to use its methods, and that's AdditiveIterator which is essentially an extension method (sum()) in C# parlance. > Further I still wonder about the file hierachy. Does that mean it's > really intended that I need to organize the files like I have shown > above? Or is there a way to export a type into a specific module > within a crate without it having to be an a specific file for that > module (which from my POV wouldn't make things more obvious xD)? No, as Daniel says, you can organize your code in (almost) any way you like and still present the same public API. This is a significant difference from the languages you know, with the theoretical (ignoring existing conventions) exception of C++. To re-export, just "pub use" it: http://doc.rust-lang.org/tutorial.html#reexporting-names You can check out the libstd source code for extensive examples. Many of the modules in its API are actually just a bunch of re-exports, often even from different crates! std::sync for example is almost entirely documentation and "pub use core_sync::*". It also defines two types (Future and TaskPool), which it puts into private sub-modules (std::sync::future and std::sync::task_pool) for organizational purposes but exports as std::sync::{Future,TaskPool}. None of this affects the API or even details of the rustdoc. > Maybe it's also just because rust is new and still missing best > practices. I just think maybe someone with a better overview should > draw up some best practices of how to organize large projects without > having large monolythic files as most rust projects do at the moment. Auron recently started an effort in that general direction, though concerning many many topics beside file organization take precedence: https://mail.mozilla.org/pipermail/rust-dev/2014-July/010735.html But I don't think the situation is as dire as you think. The organization of most rust code perhaps seems more disorganized to you than it seems to readers from other backgrounds (including the authors). Coming from Python for example, it is not at all natural to have one file per class. The boundaries of a module are a balance between small files, keeping tightly related code together (this can mean up to tens of small classes per module), and exposing a convenient, coherent API (non-issue in Rust due to re-exports). It's another consequence of not making class relationships the pillar of the design. > Further I notices that they often seem to mix test and production code > which is also strange to me. For instance with test driven development > you have tons of problems with that approach. Only mentioning a few: > > 1) Test code will most likely be at least in the order of magnitude of > the production code it tests, so putting them together in the same > directory or even file will get unhandy pretty quickly > 2) The test code this way can access internals of the module, which by > definition should be avoided, since internals are implementation > details and if your tests use them you will get cascades of test > failures for each simple design change > 3) Performance, mostly. To have fast iteration cycles, tests most > likely need to be grouped an executed/compiled intelligently (which > needs some compiler or IDE support) so that only tests that are > relevant to the changes you just made will be compiled and executed. > If you mix tests with production code then then it is a lot more > involved to sieve out tests during compilation, linking and > execution... that not need to run. > > best > chris _______________________________________________ Rust-dev mailing list [email protected] https://mail.mozilla.org/listinfo/rust-dev
