Cursed Rust: Printing Things The Wrong Way
— Let's have some fun with 'Hello, world!'
There is a famous story about a physicist during an exam at the University of Copenhagen. The candidate was asked to describe how to determine a skyscraper's height using a barometer. The student suggested dangling the barometer from the building's roof using a string and then measuring the length of the string plus the barometer's height. Although technically correct, the examiners were not amused.
After a complaint and a reevaluation, the student offered various physics-based solutions, ranging from dropping the barometer and calculating the building’s height using the time of fall, to using the proportion between the lengths of the building's shadow and that of the barometer to calculate the building's height from the height of the barometer. He even humorously suggested simply asking the caretaker in exchange for the barometer.
The question and its possible answers have an important didactic side effect: they convey to the learner that one can also get to the solution with unconventional methods — and that these methods are often more interesting than the canonical solution because they reveal something about the problem itself.
There is virtue in learning from unconventional answers to conventional questions. To some extent, this fosters new ways of thinking and problem-solving, which is an essential part of innovation.
One of the first examples in any book on learning Rust is the "Hello, world!" program.
It's an easy way to test that your Rust installation is working correctly.
However, we can also have some fun and turn the task on its head: let's find ways to print "Hello, world!" without using
Let's try to come up with as many unconventional solutions as possible. The weirder, the better! As you go through each of the solutions below, try to understand why they work and what you can learn from them.
This started as a meme, but I decided to turn it into a full article after the post got a lot of attention.
It goes without saying that you should never use any of these solutions in production code. Check out this enterprise-ready version of hello world instead.
use Write; write!;
This solution is interesting, because it shows that
println! is just a macro that expands to a call to
write! with a newline character appended to the string.
There is a real-world use case for this: if you want to print things really fast, you can lock
stdout once and then use
write!. This avoids the overhead of locking
stdout for each call to
println!. See this article on how to write a very fast version of
yes with this trick.
This shows that you can implement
println! using Rust's powerful iterators. Here we iterate over the characters of the string and print each one of them.
chars() returns an iterator over Unicode scalar values.
Learn more about iterators here.
This teaches us a little bit about how traits work in Rust: We define a struct that implements the
Display trait, which allows us to print it using
print!. In general,
Display is intended to make more complex types printable, but it is also possible to implement it for a hardcoded string!
How about we create our own trait instead of using
We can exploit the fact that we can name our trait methods however we want. In this example, we choose
println, making it look like it is part of the standard library.
This completely turns the
println! macro on its head. Instead of passing a string as an argument, we call a method on the string itself!
There are other ways to print things in Rust than using
println!. In this case, we use
panic!, which prints the string (as a side-effect) and immediately terminates the program. It works as long as we only want to print a single string...
Rust allows you to call a closure directly after its definition. The closure is defined as an anonymous function that takes a string slice as an argument and prints it. The string slice is passed as an argument to the closure.
In practice, this can be useful for defining a closure that is only used once and for which you don't want to come up with a name.
extern crate libc; use ; use CStr; extern "C"
You don't even need to use Rust's standard library to print things! This example shows how to call the C standard library's
printf function from Rust. It's unsafe because we are using a raw pointer to pass the string to the function. This teaches us a little bit about how FFI works in Rust.
We're well into psychopath territory now... so let's not stop here. If you try extremely hard, you can bend Rust to your will and make it look like C++.
use Display; use Shl; ; ; cout << "Hello World" << endl;
Shl trait is used to implement the
<< operator. The
cout struct implements
Shl for any type that implements
Display, which allows us to print any printable type. The
endl struct implements
cout, which prints the newline character in the end.
Credit goes to Wisha Wanichwecharungruang for this solution.
All of these high-level abstractions stand in the way of printing things efficiently. We have to take back control of your CPU. Assembly is the way. No more wasted cycles. No hidden instructions. Pure, unadulterated performance.
use asm; const SYS_WRITE: usize = 1; const STDOUT: usize = 1;
If you're wondering why we use Rust in the first place if all we do is call assembly code, you're missing the point! This is about way more than just printing things. It is about freedom! Don't tell me how I should use my CPU.
Okaaay, it only works on x86_64 machines, but that's a small sacrifice to make for freedom.
Submitted by isaacthefallenapple.
Why did we pay a premium for all those CPU cores if we aren't actually using them? Wasn't fearless concurrency one of Rust's promises? Let's put those cores to good use!
use ; use thread; use Duration;
Here, each character is printed in a separate thread. The threads are spawned in a loop, and each thread sleeps for a certain amount of milliseconds before printing its character. This uses the full power of your CPU to print a string! It might not always consistently print the characters in the right order (hey, scheduling is hard!), but that's a worthwhile trade-off for all the raw performance gains.
If you've got more solutions, please send me a message.
Also, if you liked this article, you might also enjoy the yearly obfuscated C code contest. Check out the previous winners here.
If you were actually more intrigued by the barometer story, read Surely You're Joking, Mr. Feynman!, a book by Richard Feynman, another famous physicist and Nobel Prize winner, who was known for his unconventional way of thinking.
We should all strive to think outside the box and come up with unconventional solutions to problems. Who knows, maybe that's the key to a deeper understanding of the problem itself?
Thanks for reading! I mostly write about Rust and my (open-source) projects. If you would like to receive future posts automatically, you can subscribe via RSS or email: