What Kotlin might be taught from Rust « Otaku – Cedric’s weblog
This can be a observe as much as my earlier article, during which I explored just a few facets of Kotlin that Rust might be taught from. This time, I’m going to have a look at some options that I actually take pleasure in in Rust and which I want that Kotlin adopted.
Earlier than we start, I’d prefer to reiterate that my level is to not begin a language battle between the 2 languages, nor am I attempting to show one language into the opposite. I spent cautious time analyzing which options I need to focus on and robotically excluded options that make good sense for one language and can be absurd within the different. For instance, it could be foolish to ask for rubbish assortment in Rust (since its foremost proposition is a really tight management on reminiscence allocation) and reciprocally, it could make no sense for Kotlin to undertake a borrow checker, since the truth that Kotlin is rubbish collected is one among its foremost appeals.
The options I coated in my first article and on this one are functionalities which I feel might be adopted by both language with out jeopardizing their foremost design philosophies, though since I don’t know the internals of both languages, I is likely to be off on a few of these, and I welcome suggestions and corrections.
Let’s dig in.
Macros
I’ve at all times had a love hate relationship with macros in languages, particularly non hygienic ones. On the very least, macros needs to be totally built-in within the language, which requires two situations:
- The compiler wants to concentrate on macros (not like for instance, the preprocessor in C and C++).
- Macros have to have full entry to a statically typed AST and be capable of safely modify this AST.
Rust macros meet these two necessities and because of this, unlock a set of very fascinating capabilities, which I’m fairly positive we’ve solely began exploring.
For instance, the dbg!()
macro:
let a = 2;
let b = 3;
dbg!(a + b);
Will print
[srcmain.rs:158] a + b = 5
Be aware: not simply the supply file and line quantity however the full expression that’s being displayed (“a + b
”).
One other nice instance of the ability of macros will be seen within the debug_plotter
crate, which lets you plot variables:
fn foremost() {
for a in 0..10 {
let b = (a as f32 / 2.0).sin() * 10.0;
let c = 5 - (a as i32);
debug_plotter::plot!(a, b, c; caption = "My Plot");
}
}

How stunning and geeky is that?
Kotlin shouldn’t be fully unarmed on this division for the reason that mixture of annotations and annotation processors present a set of performance that isn’t very removed from what you are able to do in Rust with macros and attributes. The principle distinction is that whereas Kotlin’s strategy solely permits for authorized Kotlin code to ever be current in a Kotlin supply file, Rust permits for any arbitrary syntax to seem as a macro argument, and it’s as much as the macro to generate appropriate Rust that the compiler will settle for.
I’ve to confess that my thoughts shouldn’t be totally made on this specific facet.
On one hand, it’s good to have the ability to write any type of code in a Rust supply file (that is what React does with JSX), however, the potential for abuse is excessive and one can rightfully worry a day when a Rust supply file would look nothing like Rust code. Nonetheless, thus far, my worry has by no means materialized and most macros that I’ve encountered use customized syntaxes very parsimoniously.
One other essential facet of macros is that Rust IDE’s perceive them (nicely, at the very least, CLion does, and doubtlessly, all IDE’s can, and can) and they’ll instantly present you errors when one thing goes unsuitable.
Macros are utilized in a really giant array of eventualities and supply Rust with some actually neat DSL capabilities (e.g. for libraries supporting SQL, Net, graphics, and many others…).
Additionally, macros combine very neatly with…
Attributes for preprocessing
Attributes are Rust’s model of annotations and so they begin both with #
or #!
:
#![crate_type = "lib"]
#[test]
fn test_foo() {}
Nothing groundbreaking right here, however what I need to focus on is the conditional compilation facet.
Conditional compilation is achieved in Rust by combining attributes and macros with cfg
, which is accessible as each an attribute and a macro.
The macro model means that you can compile conditionally an announcement or an expression:
#[cfg(target_os = "macos")]
fn macos_only() {}
Within the code above, the perform macos_only()
will solely be compiled if the working system is macOS.
The macro model of cfg()
means that you can add extra logic to the situation:
let machine_kind = if cfg!(unix) {
"unix"
} else { … }
On the danger of repeating myself: the above code is a macro, which suggests it’s evaluated at compile time. Any a part of the situation that isn’t meant will likely be fully ignored by the compiler.
You may rightfully surprise if such a function is critical in Kotlin, and I requested myself the identical query.
Rust compiles to native executables, on a number of working programs, which makes this type of conditional compilation just about a requirement if you wish to publish artifacts on a number of targets. Kotlin doesn’t have this drawback because it produces OS impartial executables which are run on the JVM.
Though Java and Kotlin builders have realized to do with out a preprocessor for the reason that C preprocessor left such a foul impression on just about everybody who has used it, there have been conditions in my profession the place having the ability to have conditional compilation that features or excludes supply recordsdata, and even simply statements, expressions, or capabilities, would have come in useful.
No matter the place you stand on this debate, I’ve to say I actually take pleasure in how two very completely different options within the Rust ecosystem, macros and attributes, are capable of work collectively to provide such a helpful and versatile function.
Extension traits
Extension traits mean you can make a construction conform to a trait “after the actual fact”, even when you don’t personal both of those. This final level bears repeating: it doesn’t matter if the construction or the trait belong to libraries that you just didn’t write. You’ll nonetheless be capable of make that construction conform to that trait.
For instance, if we need to implement a last_digit()
perform on the sort u8
:
trait LastDigit {
fn last_digit(&self) -> u8;
}
impl LastDigit for u8 {
fn last_digit(&self) -> u8 {
self % 10
}
}
fn foremost() {
println!("Final digit for 123: {}", 123.last_digit());
// prints “3”
}
I is likely to be biased about this function as a result of until I’m mistaken, I used to be the primary particular person to recommend an analogous performance for Kotlin again in 2016 (hyperlink to the dialogue).
Initially, I discover the Rust syntax elegant and minimalistic (even higher than Haskell’s and arguably, higher than the one I proposed for Kotlin). Second, having the ability to prolong traits this fashion unlocks quite a lot of extensibility and energy in how one can mannequin issues, however I’m not going to dive too deep into this matter since it could take too lengthy (search for “kind lessons” to get a way of what you possibly can obtain).
This strategy additionally permits Rust to imitate Kotlin’s extension capabilities whereas offering a extra normal mechanism to increase not simply capabilities however sorts as nicely, on the expense of a barely extra verbose syntax.
In a nutshell, you’ve the next matrix:
Kotlin | Rust | |
Extension perform | enjoyable Sort.perform() {...} |
Extension trait |
Extension trait | N/A | Extension trait |
cargo
This most likely comes off as a shock since with Gradle, Kotlin has a really robust construct and package deal supervisor. The 2 instruments definitely have the identical purposeful floor space, permitting to construct advanced tasks whereas additionally managing library downloading and dependency decision.
The explanation why I feel cargo
is a superior various to Gradle is due to its clear separation between the declarative syntax and its crucial aspect. In a nutshell, commonplace, widespread construct directives are specified within the declarative cargo.toml
file whereas advert hoc, extra programmatic construct steps are written immediately in Rust in a file known as construct.rs
, utilizing Rust code calling into a reasonably light-weight construct API.
In distinction, Gradle is a multitude. First as a result of it began being laid out in Groovy and it now helps Kotlin because the construct language (and this transition continues to be ongoing, years after it began), but in addition as a result of the documentation of each of these continues to be extremely dangerous
By “dangerous”, I don’t imply “missing”: there’s quite a lot of documentation, it’s simply… dangerous, overwhelming, most of it outdated, or deprecated, and many others…, requiring a whole lot of strains of copy/paste from StackOverflow as quickly as you want one thing out of the crushed path. The plug-in system could be very loosely outlined and mainly lets all plug-ins entry no matter they really feel like inside Gradle’s inner constructions.
Clearly, I’m fairly opinionated on this matter since I created a construct instrument impressed by Gradle however utilizing extra trendy approaches to syntax and plug-in decision (it’s known as Kobalt), however independently of this, I feel cargo
manages to strike a really high quality stability between a versatile construct+dependency supervisor instrument that covers all of the default configuration adequately with out being overwhelmingly advanced as quickly as your venture grows.
u8, u16, …
In Rust, quantity sorts are fairly simple: u8
is an 8 bit unsigned integer, i16
is a 16 bit signed integer, f32
is a 32 bit float, and many others…
That is such a breath of recent air to me. Till I began utilizing these sorts, I had by no means fully recognized how uncomfortable I had at all times been with the best way C, C++, Java, and many others… outline these sorts. Each time I wanted a quantity, I’d use int
or Lengthy
as default. In C, I typically went so far as lengthy lengthy
with out actually understanding the implications.
Rust forces me to pay very shut consideration to all these sorts after which, the compiler will relentlessly hold me sincere each time I attempt to carry out casts that may result in bugs. I actually assume that every one trendy languages ought to observe this conference.
Compiler error messages
To not say that Kotlin’s error messages are dangerous, however Rust definitely set a brand new commonplace right here, in a number of dimensions.

In a nutshell, here’s what you possibly can anticipate from the Rust compiler:
- ASCII graphics with arrows, colours, clear delineations of problematic sections.
- Plain English and detailed error messages.
- Solutions on how you possibly can repair the issue.
- Hyperlinks to related documentation the place you could find out extra about the issue.
I definitely hope that future languages will take inspiration.
Portability
About twenty-five years in the past, when Java got here out, the JVM made a promise: “Write as soon as, run anyplace” (“WORA”).
Whereas this promise stood on shaky grounds within the early years, there isn’t a denying that WORA is a actuality at this time, and has been for a few many years. Not solely can JVM code be written as soon as and run in all places, such code may also be written anyplace, which represents an vital productiveness increase for builders. You’ll be able to write your code on any of Home windows, macOS, Linux, and deploy on any of Home windows, macOS, and Linux.
Surprisingly, Rust can be able to such versatility, although it produces native executables. Whatever the working system you might be writing your code on, producing executables for a mess is trivial, with the additional advantage that these executables are native and, because of the unimaginable technical achievement that the LLVM is, very performant too.
Earlier than Rust, I had resigned myself to the truth that if I needed to run on a number of working programs, I needed to pay the worth of working on a digital machine, however Rust is now displaying which you can have your cake and eat it too.
Kotlin (and the JVM basically) is starting to be taught this lesson too, with initiatives reminiscent of GraalVM, however producing executables for JVM code continues to be fraught with restrictions and limitations.
Wrapping up
I’ve much more to say about all of this.
And by “all of this”, I imply “Rust and Kotlin”.
They’re each such fascinating languages. I like them each, however for various causes. I hope I used to be capable of convey a few of my fondness in these two posts. Though these articles may seem important, they’re actually love letters. I’m a really demanding developer, somebody who’s been writing code for forty years and who plans to maintain doing so for so long as his psychological skills permit him. I really feel unreasonably obsessed with programming languages, and I hope that my ardour shone by way of these two articles.
TestNG is a venture I began round 2004 with the one intent to combine issues up. I needed to indicate the Java world that we might do higher than JUnit. I had no intentions for anybody to love and undertake TestNG: it was a venture lab. An experiment. All I needed to do was to indicate that we might do higher. I used to be genuinely hoping that the JUnit staff (or no matter was left of it) would check out TestNG and assume “Wow, by no means considered that! We are able to incorporate these concepts in JUnit and make it even higher!”.
That is my purpose with these couple of posts. I’d be ecstatic if these two very, very completely different worlds (the Rust and Kotlin communities) would pause for a second from their breakneck growth tempo, take a fast take a look at one another, although they actually had little interest in doing so, and understand “nicely… that’s fascinating… I ponder if we might do that?”.
Discussions on reddit:
This entry was posted on November 9, 2021, 3:58 am and is filed beneath Uncategorized. You’ll be able to observe any responses to this entry by way of RSS 2.0.
Each feedback and pings are at the moment closed.