top | item 7650831

Rust by Example

335 points| adamnemecek | 12 years ago |rustbyexample.github.io

161 comments

order
[+] jganetsk|12 years ago|reply
I try to describe Rust to lots of people. It's astonishing to me that most people have not imagined the possibility of a language having manual memory management with the compiler checking for mistakes and guaranteeing safety. So many programmers have such a low opinion and comprehension of type systems that it's very regrettable.
[+] jeffdavis|12 years ago|reply
I am one of those people who didn't really consider the possibility. It's not that I thought it was impossible, but I dismissed it as probably impractical to use.

Rust is showing that it's actually practical, in a somewhat-normal language with somewhat-normal libraries.

[+] SkyMarshal|12 years ago|reply
Fwiw, I never appreciated type systems till I learned Haskell, which showed me that mere data types do not a type system make. Type classes, signatures based on currying, an entire language designed around sophisticated type theory, it was probably the biggest eye opener since grokking lisp's code is data is code paradigm. But most undergrad education is C, C++, Java, and maybe Python these days, and don't get me started on Rails schools. You tend not to learn this stuff unless you hang out at HN, or Reddit back in the day, or read PG's essays, or are just naturally curious.
[+] oscargrouch|12 years ago|reply
We can see a live example of that in some of the comments in this thread..

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")

[+] jganetsk|12 years ago|reply
One of the fascinating ambitions of Rust is to bring linear type systems to the mainstream. The programming world is so un-acquainted with linear types, yet the possibilities are staggering. To me, functional programming languages were always based around referential transparency: the property that a name and its value are interchangeable. But Rust takes another approach: a name is an owner of value, and owners are linear resources. This is very exciting, and makes me want to learn linear logic. It also makes me wonder if there is a smaller language dying to break out of this larger one.
[+] pcwalton|12 years ago|reply
> It also makes me wonder if there is a smaller language dying to break out of this larger one.

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.

[+] Kutta|12 years ago|reply
I'm excited about practical linear typing implemented on the top of full-dependent systems. It would bode pretty well for experimentation and research, more so than baked-in linear type systems, because of the possibility of writing new stuff without having to touch the compiler internals.

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.

[+] eranation|12 years ago|reply
This is pretty cool. To my surprise, I found many language constructs similar to Scala. Is it just me? It's like go and Scala mashed together with the good parts from both syntaxes.
[+] steveklabnik|12 years ago|reply
Rust takes more from OCaml and Haskell than Scala, but it's very inspired by functional languages, it's not just you.
[+] rm445|12 years ago|reply
There's tons of Python too, wouldn't you say?

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.

[+] bjz_|12 years ago|reply
Scala and Rust share common roots in ML and Haskell. I would say it's more like a mash between C++, ML and Haskell.
[+] pooya72|12 years ago|reply
This is cool. I'm really excited about Rust, but as a programming hobbyist(noob) I haven't been able to get very far with it. The language is starting to become more googleable now, even though the name "rust" is a hinderance.

That's one of the things I like about clojure. Very googleable name.

[+] tomlu|12 years ago|reply
As it becomes more famous Google will learn that it's a programming language and that you're a programmer. The first search result for "View" is the Android documentation for me.
[+] kybernetyk|12 years ago|reply
I guess/hope that "rustlang" (like with D and go before) will become the googleable term.
[+] asdfaoeu|12 years ago|reply
Trying to find out what rustc's oxidise step was especially painful.
[+] zephyrthenoble|12 years ago|reply
I am a CS student who had to use Rust for my OS class. I have had several issues using Rust so far, mostly caused by a lack of verbose documentation and examples. The basic ideas are great, but it's hard to learn about some things, such as lifetimes, because it isn't covered in extensive detail in the official documentation, and not at all elsewhere. The compiler is no help, because the errors aren't online.

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.

[+] conradk|12 years ago|reply
That seems odd for a student. Let me explain what I mean.

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.

[+] adamnemecek|12 years ago|reply
The bigger issue is that your course professor picked a language that's still pretty new and under development. To quote the Rust website "Rust is a work-in-progress and may do anything it likes up to and including eating your laundry".

> 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.

[+] Uehreka|12 years ago|reply
This looks great, I've bookmarked it for the next time I want to learn a new language.

Speaking of which: I haven't been keeping up with the Rust news, is it stable yet? Or are they still introducing breaking changes?

[+] jcmoyer|12 years ago|reply
Breaking changes still happen, but commits to the rust repo are tagged with [breaking-change] so you can easily do a `git log --grep breaking-change` and (hopefully) get a good idea of how to fix your code. For more gradual breaking changes, the developers have been good at adding lints to rustc so you're told how to fix your code via compile-time warnings. Vec migration and crate attribute syntax are two recent examples of this.
[+] adamnemecek|12 years ago|reply
Not quite stable (current version is 0.10) but they are trying to keep the breaking changes to a minimum.
[+] zackmorris|12 years ago|reply
A few observations knowing nothing about Rust until I read this, perhaps someone can shed some light on these:

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.

[+] masklinn|12 years ago|reply
> keywords like "let" are extra work for the programmer because the compiler can infer them automatically.

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

[+] jganetsk|12 years ago|reply
> keywords like "let" are extra work for the programmer because the compiler can infer them automatically.

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.

[+] kam|12 years ago|reply
> I’m not a fan of printf()-style parameters, because they break ordering (remarkably, php does this best with its “.” and “$” notations).

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.

[+] ben0x539|12 years ago|reply
Thanks for taking the time to write out your observations! :)

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++.

[+] xiaq|12 years ago|reply
> * Lifetimes completely confuse me, as it seems like they would be handled by garbage collection?

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.

[+] shmerl|12 years ago|reply
> Lifetimes completely confuse me, as it seems like they would be handled by garbage collection?

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.

[+] aaronblohowiak|12 years ago|reply
> Real preemptive threads it seems (as opposed to Go’s goroutines which are cooperative threads/coroutines).

Go's goroutines are preemptive.

[+] estebanrules|12 years ago|reply
I'm very interested in learning either Go or Rust. Of course I could learn both, but when I learn a new language (or anything really), I like to really immerse myself and focus.

I'm leaning towards Go but Rust is very interesting.

[+] wyager|12 years ago|reply
Having spent a fair amount of time with both, I have to say that I would suggest Rust. Go is not a well-designed language. The type system is mediocre, generic support is bad (you have to use interface{}, which is like casting to Object in Java or passing structs/objects around as void*s in C/C++), the language relies heavily on non-extensible built-in constructs like range and make(), etc. I use Go a lot, and I like it for building web services, but I really don't see the advantage for anything else.

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.

[+] steveklabnik|12 years ago|reply
Go is like a better Ruby or Python. Rust is like a better C++. Go is ready for production today. Rust will have 1.0 this year, and only has 3 production deployments so far.

Hope that helps.

[+] logicchains|12 years ago|reply
If you already know C, it would only take a week or two to learn Go, so it would be quite possible to do both.
[+] zobzu|12 years ago|reply
one of the point people are trying to make and apparently get misundertood:

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++.

[+] megaman821|12 years ago|reply
It seems number 3 is all about have solid libraries with a nice interface. If I need an API server that can handle a fair bit of traffic, having a solid, easy-to-use HTTP lib and database lib is going to go a long way. Nothing I see in Rust precludes them from delivering these things.
[+] thinkpad20|12 years ago|reply
What are some things that have been written in rust, besides Servo? I think one of the reasons Go has been so successful is that they started right off the bat with something people could use, and focused on creating tools and other things which encouraged people to dive in. Julia had this too with their math libraries. Things like web servers / frameworks, game engines, parser libraries, command line tools... What is written in rust that I could use to start building actual stuff?
[+] zura|12 years ago|reply
Nice. Similar thing for Golang would be appreciated. I mean more advanced than Go tour.
[+] kybernetyk|12 years ago|reply
This is really great and answers many questions I had about details of Rust but was too lazy to google. Thanks!

Btw. the lifetimes example misses syntax coloring.

[+] fooyc|12 years ago|reply
Rust feels a lot like C++ with smart_ptr/unique_ptr by default, just with a different syntax.

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 ;".

[+] masklinn|12 years ago|reply
> - Traits (a.k.a. I don't known where this method is implemented).

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.

[+] adamnemecek|12 years ago|reply
If you actually believe that, you have not really looked. It adds e.g. algebraic data types, pattern matching, actor based concurrency, it's more functional, has pretty good macros, has the compiler do more heavy lifting etc.

> 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.

[+] chrisdevereux|12 years ago|reply
What do people use to write Rust? Seems like it would really make the most of good editor/IDE support.
[+] elinchrome|12 years ago|reply
Well that's exciting. I had been waiting for precisely such a tutorial for rust.
[+] JetSpiegel|12 years ago|reply
Rust looks like a marriage between Java and C. Very interesting.

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

[+] masklinn|12 years ago|reply
> Rust looks like a marriage between Java and C. Very interesting.

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.