Building an OpenStreetMap app in Rust, Part II

Tor Hovland
Bekk
Published in
4 min readFeb 15, 2021

--

In Part I you’ll find the introduction to the app we’re going to build, and why we’re doing it in Rust. In this part, we’ll get an initial web app up and running.

There are lots of choices to make:

  • Are we looking for a backend web app that serves HTML (typically using Rocket) or a frontend single-page app that runs WebAssembly? Well, an app that runs on the device is what I’m after, and making it work in WebAssembly is part of the challenge.
  • Will we use a web app framework, and if so, which one? There are several to choose from, but Seed and Yew seem to be the main contenders. They’re both inspired by Elm, and I do love Elm, so… I don’t have a strong opinion about which one to choose, but I can symphatize with the arguments for Seed brought forward here, so I’ll go with that.
  • Will we setup building and bundling manually, or use a bundler? If so, do we go with Webpack or with a purely Rust-based solution such as Trunk? Well, Trunk does look promising. I suppose we’ll try that, and if it doesn’t do what we want, we’ll try Webpack instead.
Photo by Amritanshu Sikdar on Unsplash

Hello World!

First we need to install Rust, and then the WebAssembly target:

rustup target add wasm32-unknown-unknown

We’ll create a new Rust executable:

cargo new surway --bin

That’s right. “Surway” is the best I’ve come up with so far. 😀 Let's see if it sticks. Do tell if you have a better name!

Next, we’ll install Trunk:

cargo install --locked trunk
cargo install wasm-bindgen-cli

Trunk uses index.html as its starting point, so we’ll add this as well as an empty SCSS file that it refers to:

<html>
<head>
<link data-trunk rel="scss" href="src/styles/index.scss" />
</head>
</html>

At this point, we can build our bundle and serve it using:

$ trunk serve
📡 server running at http://127.0.0.1:8080/
✅ trunk | success

Trunk has compiled our Rust code into WebAssembly, added a small Javascript file that bootstraps it, compiled the SCSS code, and referenced it all in an HTML file.

When we open the served web page we get… a blank page. Not even as much as a “Hello World”. Let’s fix that.

Actually, the fresh Rust executable does intend to greet the world, but not in a way that will show up in a browser:

fn main() {
println!("Hello, world!");
}

We’ll change that to:

fn main() {
web_sys::console::log_1(&"Hello, world!".into());
}

The into() is how you convert between types in Rust. It’s necessary because log_1() takes a JsValue, not a String (or str).

We also had to install web-sys in Cargo.toml.

Now our app does indeed greet us with “Hello, world!” in the browser console!

Seed

It’s time to introduce the web app framework, Seed. We’ll add it to Cargo.toml, and then we’ll add an element in index.html with id="app" that our web app can render to.

Our main method now becomes:

use seed::{prelude::*, *};fn main() {
App::start("app", init, update, view);
}

If you’re familiar with Elm, you’ll recognize the init, update, and view functions. For now, we’ll just copy the canonical counter sample from the Seed quickstart:

type Model = i32;#[derive(Copy, Clone)]
enum Msg {
Increment,
}
fn init(_: Url, _: &mut impl Orders<Msg>) -> Model {
Model::default()
}
fn update(msg: Msg, model: &mut Model, _: &mut impl Orders<Msg>) {
match msg {
Msg::Increment => *model += 1,
}
}
fn view(model: &Model) -> Node<Msg> {
div![
"This is a counter: ",
button![model, ev(Ev::Click, |_| Msg::Increment),],
]
}

If you look through the unfamiliar syntax (assuming you’re not proficient in Rust), this is all pretty simple. In short, the view function renders a text and a button. The button fires an Increment message when clicked. The increment message adds 1 to the model, which happens to be a simple integer. This model is being used as the button text, so when it increases, you can see it on the web page. It looks like this:

Nothing fancy, but now we’re up and running with real WebAssembly code running in the browser.

In case you’re interested, all the code is on GitHub.

In the next part, we’ll add a map to our app (no rhyme intended). And then we’ll get it deployed.

--

--