Coding Hello World in Rust with Cargo

Yeah, it has to be done. Here’s the world famous hello world in Rust using cargo for everything. Spoiler alert: it sucks compared to just using rustc.

[Coding Hello World in Rust]

But First …

Step by Step

Here’s the thing, when you create a new cargo project you automatically get a hello world.

cargo new hello

That will create a hello directory that you can cd into.

cd hello
ls -l
total 8
-rw-r--r-- 1 rob rob   94 May  4 18:47 Cargo.toml
drwxr-xr-x 2 rob rob 4096 May  4 18:47 src

You can look deeper.

tree
hello
├── Cargo.toml
└── src
    └── main.rs

1 directory, 2 files

This looks pretty tame now but will get crazier after we build it but first let’s have a peek inside main.rs.

Open main.rs with your editor.

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

So we see a few things here:

Looks interesting enough.

Now let’s run the thing without building it using cargo run.

cargo run
   Compiling hello v0.1.0 (hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.23s
     Running `target/debug/hello`
Hello, world!

Yeah, that’s a lot of gibberish it puts out during the implied build process.

Notice how it changed all the stuff in your directory.

Do tree again.

hello
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── target
    └── debug
        ├── build
        ├── deps
        │   ├── hello-69b81bb88019cafa
        │   └── hello-69b81bb88019cafa.d
        ├── examples
        ├── hello
        ├── hello.d
        ├── incremental
        │   └── hello-8yyw2ts1o2f5
        │       ├── s-fbwwreeb70-7uukwx-2ddc1eo67c11c
        │       │   ├── 1qminpf97lr50s7i.o
        │       │   ├── 2om3m9l7ct1zpe5m.o
        │       │   ├── 3e6089k1332uxqei.o
        │       │   ├── 3ggt82km3sp7vtag.o
        │       │   ├── 4eiyw8npoga1yltz.o
        │       │   ├── 4kyrarsmjabhmy3x.o
        │       │   ├── dep-graph.bin
        │       │   ├── query-cache.bin
        │       │   └── work-products.bin
        │       └── s-fbwwreeb70-7uukwx.lock
        └── native

10 directories, 17 files

💬 This is where I lost a lot of momentum in my liking of Rust, but I got over it. I mean that is a ridiculously large about of crap just hanging around. Keep in mind I was used to the absolutely clean Go build artifacts.

💢 I absolutely hate where cargo puts the actual executable file.

One of the things that came out of our cargo run was an executable that we can run by itself now.

./target/debug/hello
Hello world!

Pretty much what we would expect (except for all the time it took for find it buried in target/debug). Notice that the file has had the right permissions set.

ls -l target/debug/hello
-rwxr-xr-x 2 rob rob 261232 May  4 18:55 target/debug/hello

I wonder what this think looks like inside. Let’s peek.

Open the executable file in your editor.

^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^C^@>^@^A^@^@^@p;^@^@^@^@^@^@@^@^@^@^@^@^@^@ ò^
...

Yep, definitely an ELF binary full of machine code, the same thing we would see if we had opened an executable in C, C++, or Go. That’s the good stuff. No fat, lazy, interpreter to stand between our code and the processor.

Back to making this thing useful.

Thankfully the cargo install command exists to help us out. It installs the hello executable into $HOME/.cargo/bin by default.

cargo install
warning: Using `cargo install` to install the binaries for the project in current working directory is deprecated, use `cargo install --path .` instead. Use `cargo build` if you want to simply build the package.
  Installing hello v0.1.0 (/home/rob/repos/codebook/rust/hello)
   Compiling hello v0.1.0 (/home/rob/repos/codebook/rust/hello)
   Finished release [optimized] target(s) in 0.17s
Installing /home/rob/.cargo/bin/hello
warning: be sure to add `/home/rob/.cargo/bin` to your PATH to be able to run the installed binaries

What’s this? cargo install is deprecated? How can that be? 🤪

Fine. Rather than change our $PATH to include $HOME/.cargo/bin we’ll do as suggested.

cargo install --path .
Installing hello v0.1.0 (/home/rob/repos/codebook/rust/hello)
error: binary `hello` already exists in destination as part of `hello v0.1.0 (/home/rob/repos/codebook/rust/hello)`
Add --force to overwrite

Really? Sigh. Okay. I mean, which is it? You told me to do it that way Mr. cargo.

cargo install --force --path .
Installing hello v0.1.0 (/home/rob/repos/codebook/rust/hello)
   Finished release [optimized] target(s) in 0.0s
   Replacing /home/rob/.cargo/bin/hello
warning: be sure to add `/home/rob/.cargo/bin` to your PATH to be able to run the installed binaries

Isn’t that what you just warned me about earlier was “deprecated”? Well at least we learned something today. Rust likes to torment beginners.

Let’s try the other suggested non-“deprecated” way of doing it.

cargo build
Finished dev [unoptimized + debuginfo] target(s) in 0.00s                                                                            

Well that was rather boring.

Let’s see what it did differently with another tree.

home
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── target
    ├── debug
    │   ├── build
    │   ├── deps
    │   │   ├── hello-69b81bb88019cafa
    │   │   └── hello-69b81bb88019cafa.d
    │   ├── examples
    │   ├── hello
    │   ├── hello.d
    │   ├── incremental
    │   │   └── hello-8yyw2ts1o2f5
    │   │       ├── s-fbwwpp88s7-1xxoswr-2ddc1eo67c11c
    │   │       │   ├── 1qminpf97lr50s7i.o
    │   │       │   ├── 2om3m9l7ct1zpe5m.o
    │   │       │   ├── 3e6089k1332uxqei.o
    │   │       │   ├── 3ggt82km3sp7vtag.o
    │   │       │   ├── 4eiyw8npoga1yltz.o
    │   │       │   ├── 4kyrarsmjabhmy3x.o
    │   │       │   ├── dep-graph.bin
    │   │       │   ├── query-cache.bin
    │   │       │   └── work-products.bin
    │   │       └── s-fbwwpp88s7-1xxoswr.lock
    │   └── native
    └── release
        ├── build
        ├── deps
        │   ├── hello-8840a9a8816fd06d
        │   └── hello-8840a9a8816fd06d.d
        ├── examples
        ├── hello
        ├── hello.d
        ├── incremental
        └── native

16 directories, 21 files

Holy unnecessary artifacts, Batman!

Now that is a lot of stuff just for one simple cargo build command. It’s more than a little confusing where the actual executable even is. Once again, after looking all around we find target/release/hello.

At this point I don’t know what I like least, moving around the target/release/hello or just using the “deprecated” cargo install and adding $HOME/.cargo/bin to my path.

💢 I won’t lie. At that point I really miss the extremely simple and elegant go build I’ve come to love, one command, one executable, no mess. This is the main reason Rust is not for beginners despite the value it holds as a C++ and C replacement.