Rust by Example
rustbyexample.github.io
rustbyexample.github.io
Rust is showing that it's actually practical, in a somewhat-normal language with somewhat-normal libraries.
Two decades of garbage collected languages, with fat runtimes that does everything under the hood + lazy programmers with Moore's law mentality
Note: its ok for script languages that have exacly this purpose, to be easy and ask less from the programmer.. like Javascript or Lua.. But its a shame that languages like Java have created such a bad mentality..
For system programming its not cool to abstract the machine too much.. we've learned that lesson the hard way (see the big projects like hadoop in manage languages for an example)
Those languages asks almost nothing to use them.. but you end up paying a lot more latter, because of the abstractions layers you just cant change..
You are lucky if you project starts simple and remain simple over time, but the majority of the projects are not like that..
Yes: javascript for system programming like in nodejs is a bad idea (as in: "free entrance, pay with your blood later")
> For system programming its not cool to abstract the machine too much.. we've learned that lesson the hard way (see the big projects like hadoop in manage languages for an example)
Oh master, please do say what's wrong with Hadoop, one of the best and most successful pieces of open-source infrastructure. Truly you must know of a better alternative that isn't burdened with the cancer that is the JVM.
Also, you must mean something different when you're saying system programming, because in my dictionary, Hadoop ain't it. Not a native english speaker here, must take some lessons apparently.
Give it time.
I am:
> I'd have thought that it is still too early to say,
Let me ask this diferently? how many Big mission-critical sucessfull projects you see that are not implemented in unmanaged languages like C/C++ ?? Browsers, OS, Databases , speech-libraries, Neural Nets ??
Hadoop happens to be in Java as Lucene and Sorl, because thats what Doug Cutting choose as his language,and this guy is a monster.. If Hadoop was created in something like C++, you can bet that it would have much more success than its having now (as you would probably be able to embedd even in smartphones)
Also java is almost there, because at least it has a type system.. but it started to popularize ideas from the Smalltalk, and Self, wich are good to be well known.. but nowadays, we have too many of them.. (and people seeing Rust ask for more of them?)
The problems that Rust are trying to solve, are very hard.. and we have enough choices for managed languages(java, c#, go, javascript, python, ruby).. but almost none for languages that do the core of our IT infrastructure like C/C++..
My comment was just to point out that there are a whole generation of programmers that were taught not to think about allocations, with everything automated for them.. its cool to have those tools around? of course, its nice to have choice! but not having any other options for much of what we do, and then when something cool like Rust shows up.. not being able to reconize it for what it is.. because you know.. you are expecting that any modern language must have a GC or not have generics or macros like some comments i have seen in this thread... its just plain nonsense..
Not only Rust can be used to those tasks, but also it take in consideration a lot of research in languages and type systems..
I was not saying those languages are bad per se (but used in wrong problem spaces not suited for them).. only that some developers are too much alienated and do not know what they are talking about well as their critiques shows..
edit: about Hadoop and system programming -> what is HDFS? distributed storage, distributed networks, consensus protocol, etc ? The layer that hadoop present to the platform user are definitly not system programming, but the lower levels of the platform wich is what hadoop is made of is
I don't think so, at least without sacrificing one of (a) memory safety; (b) no mandatory garbage collection; (c) race-free concurrency. Virtually every feature in Rust* is necessary to achieve one of those three goals.
* OK, not methods, typeclasses, or macros. But methods and interfaces/typeclasses in some form are more or less an expected part of every programming language that is to go mainstream nowadays, and macros are necessary for really important conveniences like println and deriving to work.
In any case, you just described basically how Rust the language works: the ref-counted mutable types are all just part of the standard library, implemented with unsafe code but exposing a safe interface. The only mutability in the language itself is through linear types.
In Idris there is some experimental support from writing effectively linear/substructural typing rules for some program state, all within the language, and using it to enforce resource usage or state transitions.
http://eb.host.cs.st-andrews.ac.uk/drafts/eff-tutorial.pdf
In its current form it's not all that usable, because Idris itself is not usable, the error messages are positively Lovecraftian, and there is a hefty performance overhead. But it could be really good when fleshed out.
It would be also great to be able to interface higher-level linear typing constructs with low-level primitives, so that we could reproduce some of the main benefits of the Rust type system, and maybe write some kind of custom "ST monad on steroids" with more control over memory.
In a way, it's good - people are discovering what works and moving it forwards. Language fragmentation often seems like a shame - because most of the time, programming languages don't fit into different niches, we just have a multitude of general-purpose languages with a few special features to argue over. There's some hope that, over long periods of time (longer than our typical careers) languages will combine good features, genetic-algorithm style, and in the end we'll get even better languages.
That's one of the things I like about clojure. Very googleable name.
But sometime in the last few years this seems to have hit an inflection point. Domain and location specific results are expected now. Where a decade ago I would have to type in "Sycamore San Francisco" now I just type in "Sycamore" and I get what I want.
This also has an effect on adwords campaign tuning. As people become trained to not include locale in their query, the keywords become fewer and more generic. You of course use location targeting but many people crafted thousands of location-specific keywords that get less and less traffic going forward.
I won't be using Rust again until well after its 1.0 release. I don't want to struggle with a language, I want to program in it. Rust just seems like it requires a much deeper knowledge of programming theory in general for one to use it effectively, and people like me would rather just write it in Python.
> Rust just seems like it requires a much deeper knowledge of programming theory in general for one to use it effectively
I mean it's on the lower end of language spectrum so yes, it requires more knowledge than say Python but it's not much more than say C.
> people like me would rather just write it in Python
Rust is not supposed to replace Python, it's supposed to replace C/C++/Ada.
I do understand that Rust is trying to become a better alternative to current lower-level languages, but my OS class is (supposed) to be about how things like kernels and schedulers work, not about college students struggling with a new under development language. So by "rather just write it in Python" I meant "rather just write it in something I already can use efficiently." I am obviously not who Rust is aimed at.
As a student myself (formerly in CS, now Software Development), I believe that getting into a new language with a nice community, such as Rust, seems like a very good way to learn from the design mistakes that the implementors do along the way, unlike with a language like C where not much is changing.
It may also be a good place to contribute, especially since docs, tutorials and simple yet useful libraries might not yet be very present even though they are needed.
Finally, you state the the compiler is no help because the errors aren't online. I agree that this is a valid point. However, I would just ask on Stackoverflow or on the #rust IRC and then contribute the answers you get back to Stackoverflow for others to see.
If I was writing my own code for my own use, and it didn't effect a grade or was part of important work, then using Rust and learning from/contributing to the community would be a great experience.
Speaking of which: I haven't been keeping up with the Rust news, is it stable yet? Or are they still introducing breaking changes?
My "Rust for Rubyists" doesn't use a ton of library code, and here's all it took from 0.9 to 0.10: https://github.com/steveklabnik/rust_for_rubyists/commit/9e5...
THE BAD:
* keywords like "let" are extra work for the programmer because the compiler can infer them automatically.
* I’m not a fan of printf()-style parameters, because they break ordering (remarkably, php does this best with its “.” and “$” notations).
* Mandatory curly brackets are also more work, although they (could) prevent subtle bugs later.
* I’m not a fan of the generics syntax, borrowed from templates in c++ (I prefer the something like the “auto” style more, shifting work to the compiler).
* Forcing the programmer to manually place values on the heap with “~” is annoying, when other languages can automatically optimize such things.
* It seems that move semantics distinguish between objects and primitives, instead of everything being an object?
* Borrow is confusing to me, because I would probably make an immutable object passed to a function as mutable create a copy (but maybe it avoids subtle bugs).
* Lifetimes completely confuse me, as it seems like they would be handled by garbage collection?
* I don’t have a problem with the “static” keyword, except that wouldn’t global immutable variables be static anyway?
* I might have gone with traits (interfaces) everywhere instead of distinguishing them from structs, because that seems to be the direction the world is going, to avoid inheritance problems.
* I’m not sure I would have distinguished closures from functions with special syntax (although maybe it prevents subtle bugs later).
* #[bench] seems a little weird (putting profiling calls in the code), although I do find myself wanting this constantly, so if they conditionally compile they could be handy.
THE GOOD:
* Real preemptive threads it seems (as opposed to Go’s goroutines which are cooperative threads/coroutines).
* UTF-8, non null-terminated strings are good (I wonder how they handle character boundaries though, especially in languages besides English).
* Some argument could be made that borrowing c++ concepts makes Rust more approachable.
Rust's println! macro also supports named format parameters, which don't need to be in the same order as the format string.
println!("{foo}: {bar}", bar=5, foo="abc");
> It seems that move semantics distinguish between objects and primitives, instead of everything being an object?Structs fulfilling the Copy trait (meaning they don't have destructors and can be duplicated with a simple memcopy) have the same copy semantics as primitive number types.
> Forcing the programmer to manually place values on the heap with “~” is annoying, when other languages can automatically optimize such things. > Lifetimes completely confuse me, as it seems like they would be handled by garbage collection?
Rust is a systems language. The language makes memory allocation and management explicit, by design. Lifetimes allow Rust to provide memory safety at zero run-time cost. They're how the programmer proves to the compiler that pointers are valid at the time of use. There is no garbage collection by default; only when you use the Rc or Gc types from the library.
Interesting misconception :)
Lifetime is used to avoid garbage collection. By expressing the lifetime of a variable preciously in the code, the compiler knows statically (i.e. when compiling instead of in runtime) when an object can be destroyed, avoiding GC overhead and offering deterministic destructure.
I strongly disagree. In my experience scope inference either completely blows (javascript, coffeescript), sucks (python) or requires language asymmetries (ruby). Either way, I've come to see it as an awful idea.
> I’m not a fan of printf()-style parameters, because they break ordering (remarkably, php does this best with its “.” and “$” notations).
Rust does not use printf-style parameters, it uses C#-style parameters[0] (also used in Python's "new" str.format[1][2]). The format placeholders can explicitly specify the parameter index (in whatever order, e.g. "{1} {3} {0} {2}") or name ("{foo} {bar} {baz}"). See third and fourth println! of the second page.
> I’m not a fan of the generics syntax, borrowed from templates in c++ (I prefer the something like the “auto” style more, shifting work to the compiler).
?
Rust already has local type inference (which is what `auto` does), generics syntax is orthogonal to TI.
> Forcing the programmer to manually place values on the heap with “~” is annoying, when other languages can automatically optimize such things.
The whole point there is to have the developer knowingly decide whether they want allocation on the stack, the heap or somewhere else (managed or refcounted heaps are/will be available[3]). Rust is aiming at C++'s niche, not Java, having the language heap-allocate by default and try to guess if it could just stack-allocate wouldn't work.
> Lifetimes completely confuse me, as it seems like they would be handled by garbage collection?
Rust's GC is optional, and most of the standard library (and as experience shows libraries and code bases) can avoid GC. The point of lifetime is to avoid both mandatory GC and manual memory management (à la C): if the compiler knows when an object completely stops being used, it can automatically and safely release even heap-allocated objects.
> I might have gone with traits (interfaces) everywhere instead of distinguishing them from structs, because that seems to be the direction the world is going, to avoid inheritance problems.
Where do data members (attributes) live if you don't have structs?
> #[bench] seems a little weird (putting profiling calls in the code), although I do find myself wanting this constantly, so if they conditionally compile they could be handy.
#[bench] is a specialisation (of sorts) of #[test], it's only compiled in when using --test, and run when passing —bench to the resulting test harness. see http://static.rust-lang.org/doc/0.10/guide-testing.html#micr...
Note that it's not a profiling call, it doesn't profile anything (unless you enable the profiler while running the bench), it only provides coarse/toplevel timing information, like python's timeit or ruby's Benchmark
[0] http://msdn.microsoft.com/en-us/library/system.string.format...
[1] https://docs.python.org/3/library/string.html#formatstrings
[2] technically it's probably Python's version, IIRC C# requires the index and has no support for name-based parameters
[3] and maybe user-provided boxes, such as a zeroing box (to store cryptographic keys) or a GPU box, or local v remote for clusters, the sky's the limit
Some comments (speaking as someone who has followed rust for a while, but isn't heavily involved):
In general, rust wants to enable people to write code with unsurprising performance. So leaving it to the compiler to decide whether a value lives on the ~ heap wouldn't be appropriate for all use cases.
Similarly, the thing about the complicated lifetimes and borrowing shenanigans is that they exist to enable safe, efficient code that doesn't require a garbage collector.
Move semantics distinguish copyable-by-memcpy types, those can be primitives or user-defined types. It just can't be something like a ~ pointer or a type with a custom destructor or whatever, since having duplicates show up arbitrarily would mess up the associated bookkeeping. Primitive types also tend to implement the Clone trait so they can be explicitly copied with the .clone() method just like user-defined types implementing it.
Syntax is always a matter of taste... I think most people are happy with "let", there's always people who'd rather have s-expressions or significant-whitspace-delimited blocks instead of curly braces but also the other way around, I sympathize with distaste for the generics syntax to some extent... in the end you can't please everybody and you could probably do worse than making your language that is aimed at C++'s niche look a bit like C++.
Go's goroutines are preemptive.
Not quite. Since 1.2, goroutines can yield on non-inlined function calls (in 1.1 it happens only on IO or on an explicit Gosched() call). A goroutine with no funcall or only inlined function calls will still block the scheduler (and the whole system if there's only one scheduler).
Until it's an issue because somebody set up CPU-bound image processing, and then by lying you've set people up for failure and you've given them a huge blind spot once things start not working correctly.
Nevertheless, they aren't preemptive, so you shouldn't say that they are. Maybe "almost preemptive"?
Apparently, the "let" keyword is needed for parsing so that Rust can have non-trivial patterns on the left hand side of the assignment operator. As in, destructuring binds. I'm assuming you don't know about pattern matching. Pattern matching is a good thing. Maybe even one of the greatest things.
> I’m not a fan of the generics syntax, borrowed from templates in c++ (I prefer the something like the “auto” style more, shifting work to the compiler).
I'm assuming this means you are not a fan of generics. Parametric polymorphism is one of the most powerful things in all of type systems. http://en.wikipedia.org/wiki/Parametricity It's deeply regrettable that not every programming language that exists today has great parametric polymorphism support (Go, I'm looking in your direction). Type parameters and type variable are CRUCIAL for this.
> Forcing the programmer to manually place values on the heap with “~” is annoying, when other languages can automatically optimize such things.
The point of the "~" is to make ownership explicit. C++'s type system is insufficient to make guarantees about ownership. It only recently gained move semantics, which enabled unique pointers, but it makes no guarantees about any other references you make to the object (with the use of .get() and non-smart pointers). As a result, you have to make sure you use C++ smart pointers with discipline. Anytime you are required to do something with discipline, that's an opportunity for a type system to jump in and check your discipline.
> It seems that move semantics distinguish between objects and primitives, instead of everything being an object?
Afaik, in Rust, everything is a primitive, and nothing is an object. What does object even mean?
> Borrow is confusing to me, because I would probably make an immutable object passed to a function as mutable create a copy (but maybe it avoids subtle bugs).
You've never passed anything by reference before? Note that borrowing has less to do with mutability and more to do with ownership discipline.
> Lifetimes completely confuse me, as it seems like they would be handled by garbage collection?
Have you imagined the possibility of a language that does no garbage collection, because it has worked it all out statically? A garbage collector is nothing more than a potentially compile-time problem deferred to run-time. When we moved type-checking from the run-time world to the compile-time world, we gained HUGE benefits in time/space performance and in informing the programmer upfront more about the potential behavior of their program. If only we could do the same thing for memory management. That's what Rust is trynig to achieve.
> I might have gone with traits (interfaces) everywhere instead of distinguishing them from structs, because that seems to be the direction the world is going, to avoid inheritance problems.
Inheritance does cause lots of problems. It's not only that, but Haskell type classes are general and cover lots of real world cases that shitty mainstream type systems can't. One simple example I give is a type-safe .equals method. Java's .equals is type checked ad hoc by the programmer (the argument is an Object, and the programmer typically uses "instanceof" and a cast). You can use generics, but it's still broken, and if someone cares I can go into more detail. Haskell type classes is the only type system I know of where you can just say "hey there's this class of types called 'Eq', where there's a method that takes two arguments of type 'self' and returns a bool". So many OOP type systems only let you have 'this' of type 'self' in a method. I'm handwaving a bit in my description here, but it's a shame this problem isn't just obvious to everyone. Rust is inspired by Haskell type classes, but not fully there.
That article is more "let's describe HKTs in Rust-y terms, so that more Rustaceans understand their purpose" than an actual proposal for implementing them in Rust. Any concrete proposal (i.e. covering implementation details and corner cases, rather than just some syntax/use-cases) would need to go through the RFC process: https://github.com/rust-lang/rfcs/blob/master/active/0001-rf...
((Unfortunately?) I imagine such an RFC would spend a month or two (or more) being discussed/thought-about.... And double that discussing syntax. ;P )
One commenter already noted higher-kinded type classes, which is the basis of Haskell's libraries and do-notation for monads. That's HUGE.
But besides higher-kinded type classes, there are others:
* Factories and constants in your interfaces. Type classes allow you to put a type variable in the return argument of a method. This lets you put factories and constants in your type classes. For example, if you look at the monoid type class:
class Monoid a where
mappend: a -> a -> a
mzero: a
And I can implement this instance Monoid String where
mappend x y = AppendStrings x y
mzero = ""
instance Monoid Integer where
mappend x y = x + y
mzero = 0
So now I can write generic code where I can get the "zero constant" of a type without knowing what type that is. I could just have a genericMonoid : T where T is a type variable, and all I know about it is that it belongs to class Monoid (maybe it's an integer, maybe it's a string, maybe it's something else). I can now get the zero value of whatever that type is. In fact, my method doesn't have to be dispatched on the type of some input argument. I can dispatch on the return value. Something like multiplyByZero : [Monoid T] int -> T
multiplyByZero 0 = mzero
multiplyByZero n = mplus mzero (multiplyByZero (n-1))
(Forgive my crappy Haskell pseudo syntax, I haven't written it in a while). Not that multiply by zero is useful.* Operators of greater arity than 1. So I hinted at this above. With "method syntax" arity is always 1. I have one "this". I can operate on it. For all other arguments to my method, I cannot actually constrain them to have the same type as "this". At best, with OOP, my method belongs to some base class, and I can insist that the non-this arguments are of base class type, but I cannot constrain them to have "this" type. This is illustrated in the monoid example above, or in the Eq example I gave, where it can be guaranteed that the two operands to == are always of the same type. In Java, the equals method has to take one argument of type "Object", while the "this" argument has the class's type.
* Multiparameter type classes. (This is behind a flag in the Haskell compiler). You can do something like this:
class Eating E F where
eat :: E -> F -> Meal
# E is short for Eater
# F is short for Food
so now I can type check to make sure that eaters eat the right type of food. instance Carnivore Meat where
eat = # specialized implementation for eating meat
instance Carnivore Vegetables where
eat = # specialized implementation for eating vegetables
instance Vegeterian Vegetables where
eat = # specialized implementation for vegeterian foodIt's also used to generate random values: http://static.rust-lang.org/doc/master/rand/trait.Rng.html
More generally, Rust supports polymorphism anywhere. e.g., The `Eq` trait: http://static.rust-lang.org/doc/master/std/cmp/trait.Eq.html
AFAIK, >1-arity polymorphism in OOP languages is generally achieved with double dispatch. But it's not as nice (or as easy to understand IMO) as in ad hoc polymorphism.
Rust does not have HKT or multi-param traits though.
RAII approach (https://en.wikipedia.org/wiki/RAII) is more efficeint than garbage collection and also way better because of predictability. I'd say it's also more straightforward. Instead of some unpredictable and chaotic background magic going on, resources are deallocated right when they aren't needed anymore. I really don't see why would anyone prefer GC to it.
I'm leaning towards Go but Rust is very interesting.
Hope that helps.
Not really as it lacks the expressive power of the said languages.
And if one is looking for execution speed there are alternatives like Cython, PyPy, JRuby and RubyMotion.
For the OP question, if he values a mordern 21st century language, Rust is the way to go, even at pre 1.0.
I personally don't find Go to be that interesting, but the number of Ruby and Python people who are making re move and are happy with it indicates that I'm an oddball.
In my point of view, Go is system oriented, concurrency builtin, compiled, lots-of-braces, few syntax features. It's like the opposite of Python and Ruby.
I would compare Go with Vala, Java and C#.
Exactly. Many people are running into issues with Ruby and Python that Go solves. Tons of Ruby and Python programmers are flocking to Go.
It's not better because it's the same, it's better because it's different.
No they aren't. If they were you would be seeing "tons" of job ads or blog posts. I've seen hardly any.
I don't think so. Go has its own set of pros/cons just like every other platform. You could equally argue that the huge array of libraries for Ruby and Python would make it "better". And ActiveRecord or Rails alone are major advantages of the Ruby platform.
Personally I see Go being more like Java 1.0. Simple and elegant.
"Clearly X is better than Y" don't do anything except incite flame wars. You can remedy the situation by explaining and showing in what areas it is better or explain what your experience of seeing the benefits are. Just leaving at "X is better than Y obviously..." that usually doesn't end well.
That is why I asked you why it was better, even thought you already answered it 3 times, because it was another chance to explain why it was better.
Really, it's all anecdata. To be clear, when I say "better," (not "clearly better" as you quoted) I mean "I see lots of people re-writing their Ruby/Python code in Go, and they're very happy with the results." Here's the most recent story I remember: https://news.ycombinator.com/item?id=7628472
Well, that's because they haven't been following the discussion, on HN and on startup circles, going on for a while.
For one, Go creators' themselves said they expected Go to attract more C++/Java people, but instead they got more Python/Ruby people. And that's like 2 years ago.
For the past year or so, there have been numerous posts about how this or that project/startup switched from Ruby/Python to Go. Last 2 weeks alone there have been around 5 such posts on the front page of Hacker News (Digital Ocean being the latest).
Go is getting increasingly used by people that were doing infastructure/backend work in Python/Ruby etc, e.g people using stuff like Twisted and Tornado, JSON services, etc.
>That is why I asked you why it was better, even thought you already answered it 3 times, because it was another chance to explain why it was better.
Well, I guess he pre-supposed people are already familiar with all the "we rewrote our system in Go and we now have X times better performance, no more gimmicks to get async, etc".
June 25, 2012: http://commandcenter.blogspot.com.br/2012/06/less-is-exponen...
A lot of the rewrites I've seen done in Go, could probably have been done in C. The thing is, for lots of problems, Go is a better C than C. You can bolt on lots of safety features on top of C, and I think you could probably be very productive and safe in C if you forgot a lot of the dangerous features that C turns "on by default". It is for example possible to use Pascal strings in C.
I don't think the comparison with Vala (or Nimrod) is too far off -- but the Go team has lots of experience with language and system design -- and I think that really helps Go be a productive tool.
Most of the projects I come across for Go are for things that traditionally have been in Ruby/Python's camp. Examples of these are web infrastructure and command-line stuff.
Rust, on the other hand, seems like its being used more for things that have been done in C++. i.e. rendering engines and operating systems.
The comment author never stated that Go is better than Ruby/Python. The author points out that a lot of Ruby/Python people have switching over to Go... seemingly more than to Rust.
This isn't a statement for or against either projects worth.
Rust, on the other hand, takes a lot of cues from Haskell/ML and is actually a very well designed language with lots of cool features like a hindley-milner based type system with strong generic support, pattern matching, strong support for immutable and functional programming styles, etc.
Of course not, but web services are the only thing that I think Go is much good for.
>And nobody is forcing you to use just one tool for all your jobs.
Does that mean I'm not allowed to talk about which tools I think are good for what?
1) i need to code something quickly, easily, safely, and i don't care for perf too much:
* i use python2
2) i need low level access, i want to manage object copy or the lack thereof, i want to fine tune memory allocation:
* i use C (or C++)
3) I want something like python, but faster, AOT compiles, with more control:
* i use Go but its not that fast and you can't manage everything
* I use C# but outside windows APIs sucks and AOT doesn't work, and unsafe mode is unsafe
* [...]
* I use rust but the syntax is crazy and its far harder than python or Go
So.. of course, rust may not be targeted at 3) - but instead as a real C++ replacement. Turns out many still want 3 regardless - even thus rust is closer to 3 than C++.
That said, rust is still complex IMO due to both specific idioms and weird/unusual syntax.
Btw. the lifetimes example misses syntax coloring.
Bad things they imported from C++ :
- Traits (a.k.a. I don't known where this method is implemented).
- Over-complicated and compile-time-specialized generics (feel a lot like templates).
- Copy traits feel a lot like C++ copy constructor (a.k.a. I never known what simple things like assignments or passing an object around will actually do) ( https://rustbyexample.github.io/examples/clone/README.html )
- Explicit allocations (stack vs heap) and ownership management.
Yet they added some bug-provoking things like "The final expression in the function will be used as return value". And crazy syntax rules like "ho, only if the expression is not followed by a ;".
> Explicit allocations and ownership management.
It has optional GC. And it's not like automatic memory management comes at no cost.
The last thing makes sense if you think of a semi-colon as an operator turning an expression into a statement. And since it's typed, it won't let you do something dumb.
My point is that they have borrowed really bad features from C++. Pattern matching, Option, etc are cool; but the C++ features are ruining it, IMO.
But above that I'm criticising features such as operator overloading, overridable copy semantics, and traits.
> - Explicit allocations (stack vs heap) and ownership management.
This is a great feature. If you're writing another web app, then sure, you don't care about stack vs heap. If you're writing performance oriented code, then you do. Both D and C# allow for a similar mechanism.
> Copy traits feel a lot like C++ copy constructor (a.k.a. I never known what simple things like assignments or passing an object around will actually do)
The assignment operator is not overridable.
> Yet they added some bug-provoking things like "The final expression in the function will be used as return value".
Yet you provide no examples on how this is bug provoking. There have been exactly 0 bugs due to this feature.
In short, it seems that you need to read about Rust in more details before making unbased claims.
> it seems that you need to read about Rust
Please keep personal slights out of HN comments. This comment would be much better without the first and last sentences.
> This is a great feature. If you're writing another web app, then sure, you don't care about stack vs heap. If you're writing performance oriented code, then you do. Both D and C# allow for a similar mechanism
Some languages do that for you. Allocating on the stack by default and falling back to the heap when it wouldn't be safe to allocate on the stack. e.g. if the object can outlive the current function call, it shouldn't be allocated on the stack.
If the Rust compiled doesn't known how to spot unsafe stack allocations, then you don't have memory safety.
> > Copy traits feel a lot like C++ copy constructor (a.k.a. I never known what simple things like assignments or passing an object around will actually do)
> The assignment operator is not overridable.
That's not what I said. Operator overloading is yet an other feature that's ruining everything else, though.
> > Yet they added some bug-provoking things like "The final expression in the function will be used as return value".
> Yet you provide no examples on how this is bug provoking. There have been exactly 0 bugs due to this feature.
I may be wrong on this one, since type checking would spot most problem at compile time.
> If the Rust compiled doesn't known how to spot unsafe stack allocations, then you don't have memory safety.
This seems like a weird thing to say given that the Rust compiler does fairly sophisticated analysis on the lifetime of data. (And yes, Rust has memory safety.)
As a low level language, the programmer decides explicitly what is allocated on the stack and what is on the heap.
It's worth something to be able to say, "put this on the stack dammit" and if it can't go on the stack safely, require that the compiler yell at you.
There are several problems with this approach for systems programming.
1. Escape analysis is conservative. Whenever you have an indirect function call or a call in another module, you have to assume that it will cause a value to escape. By making the lifetime part of the type, Rust can keep values on the stack even when indirect or cross-module functions are involved.
2. When you put a value on the heap, how do you know when to destroy it? All industry languages with escape analysis require a garbage collector, which reduces application throughput in the mark phase. But in Rust, with unique ownership, you can make that information known at compile time in most cases.
3. Not all heaps are created equal. There are many kinds of allocators—thread-local allocators, bump allocators, global allocators, etc.
4. Garbage collectors don't work very well with external libraries that don't use the same memory management scheme. By not requiring a garbage collector or a standard heap, Rust can interoperate better with other languages. For example, you can write libraries in Rust that plug into Ruby with no extra runtime support.
> That's not what I said. Operator overloading is yet an other feature that's ruining everything else, though.
Operator overloading is really important for things like bignums and custom containers like vectors. I think the Rust way of using traits for this (meaning that the operators always have consistent types, and have to be defined in one place) makes it much less confusing than the ad-hoc approach of C++.
About operator overloading, some languages have seemless bignums without allowing users to overload operators in their own classes. For containers, this could have been restricted to the index operator.
Take a look at Scala for an arguably worse implementation of operator overloading. Though still, the IDE helps a lot there.
> Take a look at Scala for an arguably worse implementation of operator overloading.
Indeed
> Though still, the IDE helps a lot there.
If I need an IDE to understand some code, and tend to stay away from that code/language.
Operators (+ - * / ^ & | ¬) come from mathematics. While I'm sure there are plenty of node jockeys who have little use for numbers outside of specifying ports, there are still plenty of us for whom operator overloading is a necessary good.
Rust doesn't have templates, but rather parametric polymorphism and traits are part of that, they are used exactly like typeclasses in Haskell.
Static typing means a dropped semicolon never introduces a bug (a meaningfully forgotten semicolon almost always results in a compile error, I've never met a problem).
In part 25 Copy and clone: "copied by default instead of moved, because these types implement the Copy trait"
So implementing the Copy trait allow to modify the semantics of assigning an object to a variable, or of passing the object around; am I right ? Feels a lot like C++'s copy constructor.
> Rust doesn't have templates, but rather parametric polymorphism and traits are part of that
This is just an other name for templates.
> Static typing means a dropped semicolon never introduces a bug
Using the last expression as return value feels weird in a language that makes everything so uber-explicit, like ownership, stack vs heap allocation, etc
They are templates with checking at the declaration, not the instantiations, so you don't get the errors nested deep inside libraries with ridiculous chains of messages.
Don't knock the semicolon thing until you try it, it's actually pretty nice. :)
That's a very good news. Indeed that section is not clear, it looks like user defined classes could implement the copy trait, which would be really bad.
Thanks for your answers.
I don't think making things explicit is a goal of rust. Making things safe as possible by default while being fas is. A good example is that most local variables are type inferenced rather then declared.
AFAIK, it's implemented either next to the struct definition or next to the trait definition, I don't think it's possible to implement a trait from one library for a struct from an other library.
> - Over-complicated and compile-time-specialized generics (feel a lot like templates).
No, Rust generics are reified (as are C#'s for instance) but they don't do arbitrary codegen, they're just generics, they're not a turing-complete compile-time metalanguage (that's macros)
> - Explicit allocations (stack vs heap) and ownership management.
Rust's very goal is to take on C and C++ with a better language, the inability to manage allocations would be a non-starter. As for ownership, the language formalises and help keep track of something which is only implied in C or C++, which is a good thing (as it moves ownership issues up, front and center, and thus reduces the likelihood of bugs due to muddled ownership).
Note that Rust has Gc and Rc containers, so you don't have to bother if you want (turns out, people like unique/owned pointers and Gc was moved from "language core" to "library" because it wasn't used that much and ended up cluttering the language for little value)
> Yet they added some bug-provoking things like "The final expression in the function will be used as return value". And crazy syntax rules like "ho, only if the expression is not followed by a ;".
Because it's statically typed, there's no possibility of a runtime bug here: `a` has type `A`, `a;` has type `()`. The compiler will tell you to get bent if you use the wrong one.
All in all, your comments read like you want something at a more abstracted level, you should look at, say, OCaml or Go. Which is fine, just not what Rust aims to provide.
They help solve ownership "problems" but can't paper over the interesting part of Rust: lifetimes and memory safety. (i.e. they stop you having to structure everything as a strictly owned tree, allowing DAGs (with plain Rc) and arbitrary graphs (with Gc and Rc + weak pointers).)
However, unique ownership will always be the thing that's easiest for humans, and more importantly, the compiler to reason about, and so shared data will always be slightly harder to work with than non-shared data.
I'm not sure why it's disingenuous, Rc and Gc are supposed to be memory safe as well, and as you note their point is simply to not bother with ownership.
> However, unique ownership will always be the thing that's easiest for humans, and more importantly, the compiler to reason about, and so shared data will always be slightly harder to work with than non-shared data.
Which, generally speaking, I'd say is an advantage not a drawback.
> Which, generally speaking, I'd say is an advantage not a drawback.
Of course, but being an advantage doesn't change the fact that Rc/Gc are not a solve-all-your-memory-management-issues tool in Rust.
That said, the lifetime system make it hard to get things dangerously wrong, so I'm quite happy with the tradeoff Rust offers.
My point was mainly that you lose much of the compiler's assistance by moving from singly- to multiply-owned data.
There's been lots of effort to make sure Rust's grammar stays simple enough to have great IDE support eventually, so as the language matures, hopefully we'll see it supported in a lot of them.
There's some error on this page. https://rustbyexample.github.io/examples/lifetime/README.htm... The syntax highlight doesn't work. EDIT: And https://rustbyexample.github.io/examples/constants/README.ht... too
Where do you get Java from? It's more of a marriage between C and ML, but it has drawn inspiration from numerous other sources.
I have to say I'm not a programmer nor I've studied Computer Science, more a Script Kiddie, and functional languages are something esoteric that I understand on a very shallow way.
That's the syntax for "generics in C-like languages" since C++, FWIW. So at its core, the inspiration there is probably C++ more than it is Java.
(Though Rust's generics really borrow more from those of Haskell, since it has more fully-featured typeclasses as opposed to just eqtypes like SML.)
Not in the browser now, but I think Rust will make a very nice compile-to-JS language with Emscripten. And this will likely be possible in the not-to-far future given how relative easy it will be to implement with LLVM.
That said, Rust's primary uses will probably be "systems programming", game dev, embedded programming, and other real-time programming.