
Rust by Example - adamnemecek
http://rustbyexample.github.io/examples/hello/README.html
======
jganetsk
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
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.

~~~
twic
They should use that on their publicity material: "Rust: it's somewhat
normal".

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

~~~
aaronblohowiak
You could if you had only linear-typed mutable and refcounted immutable.
Refcounted immutable obviously could not contain pointers to linear typed
mutable things. That would be much simpler, just as safe and require no GC.

~~~
pcwalton
Well, you'd have no multiply-owned mutable state then, which would prohibit a
bunch of useful things (such as implementing the DOM in a browser).

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.

------
eranation
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
Rust takes more from OCaml and Haskell than Scala, but it's very inspired by
functional languages, it's not just you.

~~~
eranation
I don't know OCaml much but the syntax for pattern matching, Tuples, Traits,
the fact that map, filter etc are methods on the collection (unlike F# or
Haskell) feels more like in Scala than pure functional to me.

~~~
pcwalton
Yeah, Rust tries to integrate method syntax into the language more than OCaml
(whose object system is kind of separate) and Haskell (which doesn't have an
object system). It's a different approach from Scala, though: we don't have
inheritance or virtual methods outside of traits (which are like interfaces).

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

~~~
encoderer
This. Google has been talking about personalized search for a decade. And for
years they would say that your search results were personalized but it wasn't
really obvious. You had to just trust that what you're seeing is better
because they say it is and that it's just for you.

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.

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

~~~
zephyrthenoble
You have valid points, especially about contributing back to the community.
However, I was using Rust to learn how a webserver or a kernel worked, not to
learn how a language worked.

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.

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

~~~
steveklabnik
It's worth mentioning that this policy is only a few weeks old, so it's more
of a 'will be in the future' thing than it is 'all the changes so far' thing.

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

~~~
jganetsk
> 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](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.

~~~
charlieflowers
This is very informative, thanks. If you could share some detail on how Rust
falls short of Haskell type classes, I'd love to hear it. I'm digging into
Rust, but haven't gotten that far yet.

~~~
stream_fusion
The Rust trait/typesystem doesn't currently support higher kinded
polymorphism.

~~~
adamnemecek
It MIGHT in the near future though
[http://hydrocodedesign.com/2014/04/02/higher-kinded-
types/](http://hydrocodedesign.com/2014/04/02/higher-kinded-types/)

~~~
dbaupp
s/near//.

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...](https://github.com/rust-
lang/rfcs/blob/master/active/0001-rfc-process.md)

((Unfortunately?) I imagine such an RFC would spend a month or two (or more)
being discussed/thought-about.... And double that discussing syntax. ;P )

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

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

~~~
rdtsc
How is Go a better Python?

~~~
steveklabnik
Please see the other three replies I've already typed in this thread.

~~~
rdtsc
The fact that you are getting so many "erhm, I don't think so" probably
indicates that it wasn't a good comment that promotes discussion.

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

~~~
coldtea
> _The fact that you are getting so many "erhm, I don't think so" probably
> indicates that it wasn't a good comment that promotes discussion._

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

~~~
steveklabnik
> Go creators' themselves said they expected Go to attract more C++/Java
> people, but instead they got more Python/Ruby people.

June 25, 2012: [http://commandcenter.blogspot.com.br/2012/06/less-is-
exponen...](http://commandcenter.blogspot.com.br/2012/06/less-is-
exponentially-more.html)

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

~~~
zobzu
good API is indeed a necessity - and in my opinion at least 50% of the value
of a language is the API - or probably more.

That said, rust is still complex IMO due to both specific idioms and
weird/unusual syntax.

------
thinkpad20
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
Nice. Similar thing for Golang would be appreciated. I mean more advanced than
Go tour.

~~~
dbaupp
This was actually inspired by exactly that:
[http://gobyexample.com/](http://gobyexample.com/)

------
kybernetyk
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
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](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
> \- 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.

~~~
dbaupp
I think promoting Rc and Gc in that way is a little disingenuous, since memory
safety permeates the whole design, meaning that you can never just throw
memory and mutability around without caring like you can in other languages.

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.

~~~
masklinn
> I think promoting Rc and Gc in that way is a little disingenuous, since
> memory safety permeates the whole design, meaning that you can never just
> throw memory and mutability around without caring like you can in other
> languages.

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.

~~~
dbaupp
I just mean saying "Rust has manual memory management but there's Rc and Gc to
save you from that" isn't quite true: the hard part of "memory management" in
Rust is convincing the compiler that your lifetimes work out (i.e. it's not
doing it correctly that's hard, it's getting the compiler to verify what
you've done is correct that's hard), and Rc and Gc don't really help with
that, except if you use them _everywhere_ (and the stdlib purposely doesn't
use them everywhere, so you will hit lifetimes at some point in your
codebase).

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

~~~
scott_s
I think you're in violent agreement with masklinn. His point is your point: it
is possible to avoid memory lifetime issues by using garbage collection, but
that is not idiomatic Rust, and the design of the language encourages you to
reason about memory lifetimes.

~~~
dbaupp
I'm not disagreeing with them, yes, but I do think people tout Rust's Rc and
Gc (the latter og which doesn't even exist properly yet...) as overly
optimistic solutions to escape Rust's variation of "manual" memory management:
they don't entirely free you from the shackles of the borrow checker, and even
introduce worse lifetime issues (failure at runtime as soon as (certain types
of) mutability is required).

That said, the lifetime system make it hard to get things dangerously wrong,
so I'm quite happy with the tradeoff Rust offers.

~~~
pcwalton
If you use RC and RefCell properly, there shouldn't be dynamic borrow failures
any more than there are ConcurrentModificationExceptions in Java or
NSEnumeration failures in Objective-C. Sadly most people don't use them very
effectively; it seems more work is required to make the right patterns more
obvious and easy to use.

~~~
dbaupp
Of course, if one uses things right then stuff works fine... It's relying on
people to do things right that's tricky (which is why we have Rust).

My point was mainly that you lose much of the compiler's assistance by moving
from singly- to multiply-owned data.

------
chrisdevereux
What do people use to write Rust? Seems like it would really make the most of
good editor/IDE support.

~~~
steveklabnik
We have a lot of vim and emacs people, and an IDE crew too. I personally use
vim. I think there's even a Kate file in the source tree?

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.

------
elinchrome
Well that's exciting. I had been waiting for precisely such a tutorial for
rust.

------
JetSpiegel
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...](https://rustbyexample.github.io/examples/lifetime/README.html)
The syntax highlight doesn't work. EDIT: And
[https://rustbyexample.github.io/examples/constants/README.ht...](https://rustbyexample.github.io/examples/constants/README.html)
too

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

~~~
JetSpiegel
Generics mostly, the syntax is the same. Structs with associated methods are a
rudimentary form of classes.

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.

~~~
pcwalton
Well, you're not wrong--Java's generics and Rust's generics were very
influenced by ML's. So really you're seeing the ML influence :)

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

~~~
JetSpiegel
[http://tvtropes.org/pmwiki/pmwiki.php/Main/OlderThanTheyThin...](http://tvtropes.org/pmwiki/pmwiki.php/Main/OlderThanTheyThink)

------
sholanozie
What kind of application is Rust well-suited for?

~~~
buster
Pretty much everywhere where you see C/C++ i'd say. So not in your browser,
but everywhere else.

~~~
jnbiche
>So not in your browser, but everywhere else.

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.

------
DonGateley
Really naive question: might it be possible to bridge Rust to some older
language's high content, well developed library?

~~~
kam
Rust can use C libraries pretty easily, and there's a tool to autogenerate
Rust `extern` declarations from C headers: [http://static.rust-
lang.org/doc/master/guide-ffi.html](http://static.rust-
lang.org/doc/master/guide-ffi.html) [https://github.com/crabtw/rust-
bindgen](https://github.com/crabtw/rust-bindgen)

------
gmrple
I feel like they should have named the compiler 'rustic'.

~~~
JetSpiegel
Even better, 'crust'

------
dubcanada
All we need now is a GUI library and we'll be set.

------
thikonom
does anyone know the coloscheme used in the snippets ?

~~~
caipre
It's using highlight.js, but doesn't seem to be one of the default styles. [0]
Maybe it's custom?

[0]:
[http://highlightjs.org/static/test.html](http://highlightjs.org/static/test.html)

------
mamcx
By the way, this was done with what? It look great.

~~~
steveklabnik
[http://www.gitbook.io/](http://www.gitbook.io/)

------
Keats
Is it only me or the comments are completely unreadable due to their color? I
need to highlight them to read properly

