📕 Hands-on Rust is now in print

I'm thrilled to announce that I've written an introductory book about Rust, and it's now in print: Hands-on Rust: Effective Learning through 2D Game Development and Play. It's available in full-color paperback and e-book from The Pragmatic Publisher, worldwide online book distributors, and your local bookseller (ask for ISBN 978-1680508161).

You can browse the source code in this github repo.

I truly started to love Rust when I created the Rust Roguelike Tutorial. I'd used a lot of languages in the past, my most recent being C++ ---and Rust felt like the power of C++ combined with clever systems to make it harder to shoot my feet off. Ever since I started writing BASIC in the 1980s (on a BBC Micro), I've found that writing small games is the best way for me to learn. As much as I try, I just can't digest a big manual and start being productive---programming really gels for me when I try and make something, frequently referring to the manual and online groups. Rust is nicely documented, and the community is awesome. I quickly found I could get stuck, ask for help, and people appeared with PRs fixing my issue AND explaining where I became confused.

Hands-on Rust is my attempt to share my preferred way to learn a language, so it teaches game development carefully intertwined with Rust concepts and practical examples that use them. Targeted at readers who have a little experience with other languages (enough to understand variables and basic control flow), it starts at the beginning with Rust and brings the reader up to intermediate concepts. It's deliberately pragmatic in its approach: it teaches concepts as part of working examples (source code is provided for most steps along the way), inviting you to follow along and learn the concepts by using them.

There is a little overlap with the Roguelike Tutorial, but Hands-on Rust is 100% new code and work. The tutorial is designed to make a good roguelike; the book is designed to teach Rust.

Hands-on Rust starts out helping you to install the Rust toolchain, teaching Cargo, Clippy and basic development workflow. Then it dives into some console-based examples designed to teach control flow, variable management and basic IO. That's enough to make your first game, so we dive straight into making Flappy Dragon---a Flappy Bird clone. It starts out in ASCII mode, gradually adding features in a step-by-step hands-on fashion.

Then the book starts on its big example, a roguelike-style dungeon crawler. You'll cover:

  • Writing a design document.
  • Organizing your code and making a simple map to walk around---with graphics.
  • Getting started with ECS (it uses Legion), and composing entities from components.
  • Understanding control flow and state management by making the game turn-based.
  • Add health and simple combat, solidifying your understanding of systems and introducing traits.
  • Implement end-game states (winning and losing).
  • Add field-of-view support, which is really teaching you how to consume traits.
  • Utilize various procedural generation techniques to build your maps, while learning to wrap your generators in your own traits.
  • Theming your map, which is a bit about graphics, and also about solidifying your understanding of traits.
  • Add inventory and power-ups, showing you one approach to inventory management in an ECS.
  • Design multi-level dungeons, with stairs.
  • Add data-driven design to your game, defining your games' entities in TOML and learning to use Serde to read and process it.
  • Finishing touches including packaging your game.

Not everything I wanted to cram into the book made it, due to page constraints (publishers don't like it when you hand in a War and Peace size tome), so expect some additional content to appear on hands-on-rust.com.

One question that comes up a lot: there is some overlap between the book and the Roguelike Tutorial. The book is 100% new content, but since they both deal with a roguelike game---some overlap is inevitable. The tutorial is focused on making a roguelike, while the book is all about teaching Rust, and making the learning process fun.

Thanks for being such a wonderful community; the friendly, inclusive nature of the Rust community really made getting into Rust fun and easy.

21 Likes

Looks great, I'm definitely interested. Does the code work on Windows, Linux, or both?

1 Like

It's been tested with Windows, Mac and Linux.

1 Like

I do not know if this is considered off topic -- I recently read Specs and Legion, two very different approaches to ECS . Your roguelike tutorial uses Specs, while your book uses Legion. I am interested in hearing your thoughts on this.

1 Like

Amusingly, if Bevy had existed when I started writing the book (long process - nearly 2 years, including tech reviews), I'd have probably used it.

Specs and Legion are both great ECS providers. Specs was one of the first ECS crates for Rust, and definitely the first I encountered that hit all of the major features. When I started the Roguelike tutorial, Specs was pretty much the only game in town - so I went with it. Zero regrets, and it works really well. Unfortunately, the fellow behind Specs hit the "this has gone as far as I can take it" wall - handed it to Amethyst for long-term support and stopped adding to it. (Amethyst are awesome, and have done a great job - they also help me host the tutorial. So I'm not knocking them in any way).

When I started with the tutorial, Legion was just starting to appear. It was spectacularly fast, and a total pain the rear to use. HECS actually started out as "Legion, for humans" if I remember rightly. I got a few examples up and running with it, but I didn't feel a compelling need to switch - I didn't need the performance boost, and it was hard to use correctly.

Fast forward a bit, and I'm starting the book. I'd chatted a bit on various Discord servers (including with Tom, the brain behind Legion) - and Legion is about to become a significantly better ECS. Proc macros were in the works to make systems easier (and teachable!), the "everything must have a tag" requirement was going away (it worked ok, but it was pure pain to teach people to use it right), queries were gaining a massively better syntax, and threading became optional (for WASM land). So I embarked upon the book using Legion. A few chapters in, the Legion improvements hit and I rewrote a little bit of content to use them. It's a pretty slick experience now.

The big underlying difference between them is that Specs uses very generic data storage, while Legion is archetypal. Specs gives you a lot of freedom to add/remove components as you wish. There's basically no performance penalty to suddenly attaching a WantsToGoToTheMoon component to an entity that has never had one before. That's awesome for a lot of things, and gives you a lot of flexibility. It also gives you a lower performance ceiling, because a join query requires a bitset lookup for every entity that might match the query. Conversely, Legion stores entities with identical component sets together (the stored entity data is called an "archetype"). So if your query matches an archetype, there's basically no overhead in returning it in a query. There's also a massive cache win, because the data you are retrieving is almost certain to be stored together. The (big) downside is that if you suddenly attach a WantsToDrinkCoffee component to an entity, the entire entity is yanked out of its current archetype and moved into a new archetype storage set. So the "lets just keep adding components" model doesn't work as well.

The book gets around the archetype issue by making intent objects into their own entities. So "I want to move north" is actually an entity with a WantsToMove component (storing who wants to move and where they want to go). This preserves performance, at the expense of making more entities (Legion can handle a lot of entities, so don't worry too much there). In the tutorial, I'd attach WantsToMove to the entity that wants to move. It's a little cleaner, but incurs a fair amount of overhead.

As an aside, I plan to write up an article on ECS in general. You really can get by with just using the "EC" part (Entities composed from Components), and do the "systems" side however you want. It could be message passing, tick functions, a mixture - they are all perfectly good and valid ways to make your game tick. Entities/Components fit really well into the Rust way of doing things, they largely avoid borrow-checker and reference issues, largely elide lifetimes (I barely had to specify one in the book), and are a great way to operate in a non-OOP language. Systems are, in my opinion, a bit more controversial. They provide a great way to schedule things and get automatic concurrency - but they don't fit every game. If your game relies on serial processing of events that MUST happen in order, you spend a fair amount of time working around the systems "let's process everything that matches this query" approach.

Hope that helped!

8 Likes

Just wanted to say this is brilliant. The main negative I took away from that article comparing Specs vs Legion is that in Legion, if you add component N+1, it requires looking up the archetype of the N components, doing N vec.pop(), then doing N+1 vec.push(). It's really elegant, you solved this problem just by adding a 'layer of indirection'.

1 Like

This is moving even further away from your book release, but I don't often run into ECS experts: Can you offer insights on why ECS is rarely used outside of game engines ?

The typical argument (which I don't find convincing) goes something like this:

  1. ECS uses struct-of-arrays instead of array-of-structs
  2. When you only need a few "columns" of a struct, array-of-struct is not memory efficient because you often skip over read memory. struct-of-array is efficient because you only read what you need
  3. Game engines care about performance and use struct-of-arrays
  4. For other problems, ECS is overkill. [This is the least convincing part of the argument].

IMHO, in most programs, there is some blob of state that is passed around. Either (1) we are mutating it or (2) even if we are using purely-functional approaches, we are returning a new immutable object that replaces the old one. This statement is often ad-hoc and inflexible, whereas ECS (atleast the EC part) seems to provide a very structured approach to it.

This is clearly not the case. Thus -- any insights on why ECS is rarely used outside of game engines?

1 Like

Wow, that's a big question! I think there's a little bit of a cultural issue here - you really don't run into many "serious" programming tutorials that reference setups that are considered game-oriented. I'd love to see more of an overlap (both approaches have a lot to offer). Otherwise, I can think of a few topics that spring from this.

A lot of business applications use a database for the state blob. When I'm explaining the "EC" part of ECS, I like to point out that it is a database---just in memory and very fast. Just like an SQL database, it divides components up (tables), typically linked via keys (entities). ECS queries are a LOT like SQL queries with joins---they pull in data from the database, combine related items, and return it together. (I saw a project a while back that implemented an ECS with SQLite's in-memory mode. It was a little sluggish, but it worked really well). So I think for a lot of line-of-business applications, the database takes on the same role as the ECS in a game---a centralized, performant (hopefully) and query-friendly repository of overall state. (You could extend the analogy by comparing systems and stored procedures).

So your classic MVC (Model-View-Controller) model is closely related to an ECS. It's just a different physical mechanism for storing the data.

The next type of business app I run into all the time takes one or more blobs of data in, and transforms it into one or more other blobs of data. For example, I recently helped a customer with a program that took their customer database, located customers and wireless systems in a US census tract shapefile, and spat out FCC compliant reporting data. This probably would benefit from a stricter ECS model for performance, but it was easier to view it as a serial process. Read data, run comparisons for each customer, spit out results. Like a lot of programs of its type, it only runs once every six months - so the customer really doesn't care about performance, so long as the results are accurate. So - applying the "keep it simple" concept - I just went with straightforward structs and glue to read from the data sources.

Where I think ECS approaches could help the most are real-time systems, with a lot of moving parts. I haven't done any Fintech work, but talking to people who have - a lot of the concepts they use sound like they are pretty close to gamedev.

Hope that helps!

1 Like

I can't help thinking it's because it's more natural for people to think about things and collections of like things. Typically a thing being represented by some properties in a struct/class and a collection of things being represented by a Vector or whatever data structure that holds those structs as elements.

In contrast to thinking about a thing as a disjoint collection of parts stored in separate arrays and some how connected together by indices.

For example we think of a cat, as represented by a struct, and a bag of cats as represented by a Vector or whatever of cat structs.

As opposed to different cat properties being stored in different arrays of each property. And an actual cat only existing as relation between those property elements in the various property arrays.

Aside: Oddly enough back in the days when BASIC had no structs we had to create collections of complex things by storing their simple parts in separate arrays. We used ECS!

1 Like

Is it tested with Windows in general or with Windows 10 specifically. The publisher's homepage states " * A computer running Windows 10, Linux, or Mac OS X." as "What you need". I am still happily running Windows 8 and 9. Is the book not for me?

It will be very hard for you to run Windows 9 :wink:

I've only tested with Windows 10 (as well as Mac OS and Linux), since that's what I have available. It should be fine with Windows 11, also.

There's no reason why it shouldn't work on Windows 7 & 8; it uses Win32/64, not WinRT (via the winit crates) - so I'd expect that to work. It requests a very tame OpenGL version (via glutin, 3.2 IIRC). I just can't say for certain that it will, because I don't have an old Win7/8 system to test on.

Right. I knew I had Windows 8 on one Laptop and one version later on the other laptop :wink:
Still good to know the book is good for everyone.

Amazon is now showing me "buy now" buttons rather than "pre-order" - so hopefully people will be getting the printed edition soon. I received my author copies, which was a very emotional and exciting experience. My lovely wife recorded the unboxing.

3 Likes