Just another programming blog

Factual accuracy is not guaranteed

Rust: Cursed hello world


Let’s start of with the simplest approach and work down from there…

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

Well that’s it folks, time to pack-up shop. I’m joking of course, I think this is pretty solid and the maintainers must think so too because this is the file cargo generates when you ask for a new project. The benefits are enumerable, it’s fully cross-platform (except for embedded devices – let’s not go there), it’s expandable, it’s easy code to read, it’s compact and so on… It’s very easy to see what the intention is here, even if you’ve never written rust before you can probably guess that print means text is supposed to pop out of somewhere when we run it.

Suppose our CEO in their infinite wisdom decides that all print statements should have their new lines explicitly in their strings… Alrighty then, let’s use print! instead.

fn main() {
    print!("Hello world!\n");
}

Fine… I’ll admit this still looks rather lame, surely we can do better than this. Let’s also say we work at a company that has a minimum word limit for source code (thank god this would never happen in real life right?), how can we make this more bloated? Well, we could start by manually expanding the print macro ourselves. My IDE says it expands with this horrid one-liner

Unfortunately, this doesn’t compile. The compiler complains about us using the `_print` function that may be removed at any time. it seems, we’re gonna have to put some real effort into this product en-shitification process. Fortunately for our overvalued company, we have other means to this ends!

fn main() {
    let message = "Hello world!\n";
    let mut stdout = std::io::stdout().lock();
    stdout.write_fmt(
        format_args!("{}",message)
    ).unwrap();

    std::mem::drop(stdout);
}

Here, we manually manage the thread-locking. You see, normally, println and similar macros take care of the thread-locking of the terminal output for us, however rust still exposes these lower level APIs in-case we do need to have more control over this nuance, we don’t at all need it here. For a cherry on the top, we are allowed to explicitly drop things in rust, and here we can abuse that fact to release the thread in exactly the same place it would happen implicitly. You may also appreciate the message itself being moved to its own variable, taking up a whole line with countless characters!

So, let’s say you have put in your 2-week’s notice but you weren’t fast enough to dodge your boss’ next crime. Your boss tells you, that cross-platform support is a huge burden to your company and this person has come up with a unique and innovative solution to it; the CEO decrees that this will start by making all print statements linux-dependent. You wonder if your boss is suffering from psychosis. You sigh in resonse because you’re tired.

use std::ffi::{CString};
use libc::{printf};

fn print(string: &str) {
    let message = CString::new(string).unwrap();
    unsafe {
        printf(message.as_ptr());
    }
}

fn main() {
    let message = "Hello world\n";
    print(message);
}

Here we use this platform’s default implementation of the c library to print the string. It’s wildly unsafe but it’s arguably platform-dependant. Your boss however doesn’t see eye-to eye and begins making obnoxious remarks and you are sent back to your desk. So, you do it again except this time, you use a system call only linux and linix-like systems have.


use std::ffi::c_void;
use std::io::Write;
use libc::write;

fn print(string: &str) {
    unsafe {
        write(1, string.as_ptr() as *const c_void, string.len());
    }

}
fn main() {
    print("Hello world!\n");
}

We use the posix write function and somehow it works and it works reasonably well. The hypothetical boss is furious, they mumble something and lose their footing. Their face becomes is pale as their body settles flat on the floor. Out of mercy, you call an ambulance and find someone who knows CPR. The situation looks suspicious, so you get back to work to avoid any additional attention on you. You think the boss said something about using rustc and no crates, you’re confident he’s gone mad but you need to keep busy until the day ends so you humor this request.

You come up with something very unique…

use std::convert::TryInto;
#[cfg(all(target_os = "linux", target_arch="x86_64"))]
mod sys {
    extern {
        // https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md#x86_64-64_bit
        // write unsigned int fd, const char *buf, size_t count
        pub fn write(fd: i32, buf : *const u8, count:u64) -> i32;
    }
}

fn write(string: &str) {
    let count: u64 = string.len().try_into().unwrap_or(0);
    unsafe {
        sys::write(1, string.as_ptr(), count);
    }
}

fn main() {
    let message = "Hello world\n";
    write(message);
}

// compiled with `rustc src/main.rs  -C default-linker-libraries=yes`

The boss is hospitalized, the acting CEO bins your project and you cry yourself to sleep (in this story, at least).

So there we go, that’s the most offensive hello world in rust I could muster. It is portable to all of 1 platforms, it’s unsafe, it’s verbose, it’s inflexible, it’s unnecessarily low-level, it uses unofficial bindings, etc. I’d like to think it’s a reverse work of art. I hope you enjoyed reading.


Leave a Reply

Your email address will not be published. Required fields are marked *