Rust's Object System
smallcultfollowing.com
smallcultfollowing.com
What's the compilation speed like?
C++'s tendency to stick template functions in header files is a curse on compilation speed compared to C. Rust's solution is apparently to compile all modules in a crate together, which at least avoids instantiating the same templates more than once when compiling from scratch, but sounds like it would make incremental compilation times even worse. I am interested in Rust, but slow compilation drives me crazy.
(And what are the rules for importing classes between crates - how do template functions work, are there any guarantees for changes to a class that won't prevent code that was compiled with an old version from working?)
You can draw the boundaries between compilation units however you'd like. If you want incremental compilation, use many crates; if you want easier dependency management, use fewer crates. By default, crates are loaded dynamically at runtime, but you can statically link crates together and inline across crates for production.
The Rust compiler tracks breaking API changes and updates your crate UUID automatically. You can change private APIs and dynamically linked code as you'd like without causing dependent crates to be recompiled, but changing an inlined template function will cause your crate UUID to change, which triggers recompilation of dependent crates. The UUID is essentially just a large hash value derived from the signatures of all the public items in your crate.
At the moment, this is unimplemented, but it's intended that you be able to change the bodies of non-inlined generic functions without causing recompilation of your callers. This works because a non-inlined generic function can only be called with pointer types, which means we can generate one piece of code for the generic function and use that. Inlined generics can be called with any value and the code is duplicated for each value you called it with, so changing the body of one of those does trigger recompilation of dependent crates.
and then you mix it into the class in question: class SomeClass { mixin ExtraStuff; }
It also gives you a lot more flexibility: you can mix it not only in classes but in structs(in D those are different types), to the global scope and even inside the functions(in that case someData becomes a local variable and someMethod() an inner function).
Edit: Oh, and also the D programming language. There are quite a few of these!
Rust seems a different level of complicated to Go. Go seems quite minimalist. Rust seems more like it's trying to include the essentials of everything, but in as concise a fashion as possible.
When in doubt regarding features, I tend to ask myself "would a next-generation browser engine need this language feature to be more secure/more maintainable/faster than current browsers?" (That's not to say that we're building the language only for a browser engine, but I think it's useful to have a target use case in mind when designing a language.)
Go reminds me a lot of C as far as the look and feel is concerned. Go code is usually readable and straightforward, simply because Go (like C) deliberately limited the number of language constructs.
In contrast, Rust looks like a re-invention of modern C++.
I mean, C++ is much more popular than C for application development so that might not be a bad thing. However, I admit being in the C/Go camp of "build complex things out of a few simple building blocks" vs. the C++/Rust camp of "build complex things out of complex things".
That said, I think Rust is going to be somewhat more complex than Go because our needs are different (particularly around safety and memory management).
If you do see ways to simplify the language, we'd like to hear about them.
RAII and the ability to deterministically manage resources has always been C++'s killer feature IMHO.
There have been lots of improvements that could be made to C++ once you were willing to eliminate backwards compatibility - but all of the options up to now (e.g. D, Go), forced a GC on you, with no language support for explicit resource management.
I'm very pleased with direction "Rust" seems to be taking in this respect.
That's a lie. D supports both RAII(via scope stament which is a lot more flexible that C++ approach as well as using structs) and deterministic memory management.
As I understand it, the Rust GC uses reference counting precisely for this reason. To be honest, I'd be happy even if it did not detect cyclic references as I never found these to be a problem when writing C++ code (could always re-factor to avoid them).
Maybe I should have said "deterministic destruction by the GC" instead of RAII.
Whatever, my intention was not to mislead. Please don't call me a liar just because you disagree with something I state with honest intentions.
In C++, I can embedded a "socket" class as a member of some other object - on the stack or the heap - and it automatically and implicitly gets cleaned up correctly and deterministically, whether or not an exception is thrown, without me having to write any extra boilerplate.... so long as I follow the "rules" (and this is one of the places where C++ could be improved on).
"try/finally" or some sort of "stack-only" annotation doesn't really cut it for creating new resourceful objects out of existing ones.
For example, C# has the "using" syntax and also supports try/finally - these let you write code that works with anything that implements "IDisposable".
It handles the trivial cases - but as soon as you start wanting to compose together IDisposable instances you end up having to write lots of your own boilerplate to deal with propagating the "dispose" methods and ensuring that everything works correctly if an exception is thrown.
Of course, writing and testing all of the edge cases is very tedious and often difficult because you are dealing with resources like sockets and database connections after all - so even "mocking" them becomes hard.
Because its so difficult, most people just end up writing C# code that leaks resources (but not memory).
I think it might be possible to have composability in RAII in Go if such objects weren't allowed to live anywhere but the stack. (Which would limit passing through channels.) Those objects would have a well defined creation and destruction time. While not completely universal, this might still be quite powerful and useful.
You do have to be careful to avoid bringing back the objects that your finalizer references from the grave. If you do that in Go and create cycles, it leaks (which isn't unique to Go, many other languages do this). You need a sophisticated GC (like Java's) or a type system that's aware of the heap/stack distinction of objects (like Rust's) to mitigate that.
Often times, a language construct that can be defined in the fewest lines will be more readable. So the end result will generally be a more compact representation. But things like "iface" for interface strikes me as the result of designers who have their priorities mixed up. It's not even an abbreviation, it's just awkward (fn on the other hand has a long history of being an abbreviation for function).
> But things like "iface" for interface strikes me as the result of designers who have their priorities mixed up.
Disagree with you here. `iface` is no harder to visually parse as "interface" than `&&` is to visually parse as "logical and". You should try using the language and see if it's still a concern after ten minutes. If so, make a posting to the developer mailing list (https://mail.mozilla.org/listinfo/rust-dev) and let them know. The developers do care about pleasing syntax, but be warned that the language is still in such a larval state that they don't really spend much energy bikeshedding.