Rust for JavaScript Engineers - Building Connect-4

tauseefk August 19, 2025
Source

Connect Four | Repository

[Edit] After receiving some generous feedback from my friend Tim, I've made some changes to clarify confusing topics, and added another post that should make it easier for first time Rust users to get started here. If you're already familiar with Rust project structures, and have written some amount of Rust, please continue reading.


When I first wanted to learn Rust in 2017, I had no idea where to start. I had written some C starting back in 7th grade, however I wasn't particularly good at it. The only language I was competent at was JavaScript, and there weren't a lot of resources that bridged the gap between JavaScript and Rust.

My hope with this series is that it'll allow people familiar with JavaScript to incrementally adopt Rust.

There are a lot of similarities between the ecosystems surrounding these languages. As all the Rust code is compiled to WASM, it can open up a path for adoption into existing JavaScript project workflows, hopefully lowering the barrier of entry.

There are also a lot of differences between these languages, some of which might be out of the scope of this series. I'll add links to more information when encountering jargon that might not make the most sense to somebody unfamiliar with the Rust ecosystem just yet.

In this series I go over building a small browser based Connect-4 game, which uses Rust compiled to WebAssembly for the core gameplay mechanics and state management. This is very much a follow along, as that's what I find most effective for information retention. Here is the accompanying repository, that I'll use for reference. It can also serve as checkpoints in case the readers find their own implementations diverge.

Directory Structure

This is the general directory structure I use for building applications with Rust and JavaScript. I've found that this works best for building the rust crates [1] into packages that can be directly consumed by the JavaScript projects.

Here is the definition of Cargo workspaces from the Rust Book.

"...you might find that the library crate continues to get bigger and you want to split your package further into multiple library crates. Cargo offers a feature called workspaces that can help manage multiple related packages that are developed in tandem."

It's similar in nature to a yarn workspace where different npm packages can share project dependencies.

It basically means that we're going to compile the connectors crate for web target into the JavaScript project's directory so it can be imported.

In JavaScript all non-primitives types are passed by reference, but in Rust you have to specify whether something is a reference or a value. For more details on this topic, check out this section of the Rust Book. Will come back to the into call at a later point, but here's what Rust by Example has to say.

The client side code will be the most familiar to JavaScript engineers, it's a single JavaScript file that starts an http server using node and serves the index.html.

Let's look at the javascript code:

Running the code

Now that the project structure is somewhat out of the way, let's trying running the code.

If you've been following along so far, open up http://localhost:8000 in your favorite browser, "Hello, WASM!" should be logged in the console.

Connect Four

At this point we've (hopefully) established a good baseline understanding of different parts of the codebase, we can start working towards the connect four game.

Let's look at the crate changes first.

engine.rs

This is the module which will hold all the data structures relevant to the gameplay. The most important part of connect-4 are the states of each position. All three configurations "empty", "red", and "black" can be stored as variants of an enum. And as we need to import this enum into the entrypoint lib.rs, I'm declaring it using a pub keyword. This is necessary as data in Rust is private by default.

lib.rs

Using engine.rs in the entrypoint lib.rs.

Now to the significant bit. I've updated the say_hello function to declare a few variables with the let keyword and assigned enum variants. The let keyword is different from JavaScript in that it by default doesn't not allow re-assignment to those variables.

What happens on compiling the crate via the build.sh?

I get a few errors, including the following:

This is the one I really care about:

Let's go ahead and implement the trait for TileType.

Compiles without a hitch! And the browser console now logs:

Traits are Rust's way of defining shared behavior. They are more akin to composition via object extension than classical inheritence.

The trait fmt::Display requires that the function fn fmt be implemented, which gives our enum TileType the ability to define how each of its variants are to be formatted when using the format! macro.

Let's try one more thing, and implement a method on our enum directly called is_empty.

Enums in Rust can also have methods. This allows us to check if a particular TileType variant is empty, like the following:

After re-compiling, the console should now log four statements:

Remember that this method is associated to the concrete instance of the enum, so we'd call the method on the variant instead of the enum itself.

Here's the complete diff.

In the next post we're going to look into rendering the Connect-4 board as HTML.

Discussion in the ATmosphere

Loading comments...