
Author of “Unix in Rust” Abandons Rust in Favour of Nim - dom96
https://github.com/ckkashyap/rustix/issues/8
======
acomjean
Basically he likes the way Nim compiles to C fairly cleanly. Helps him because
he knows and understands C, and reading between the lines he wants to target
systems that already have C compilers, but not Rust ones.

He seems to like Rust it just likes Nim better, feels currently its better
suited. Actually fairly calm about it. Different people prefer different
languages for a variety of reasons.

Not sure compiling to C as an extra step is going to do to reliablity or
performance in Nim but we'll see. Interesting times.

~~~
aidenn0
> Not sure compiling to C as an extra step is going to do to reliablity or
> performance in Nim but we'll see. Interesting times. Having worked with
> languages that target C, machine code and C, I will make a few predictions:

Compiling to C shouldn't hurt reliability anymore than compiling to LLVM.
Performance will likely be somewhat less, particularly as "readable C" is the
target, not arbitrary C.

> Basically he likes Nim compiled to C better than just Rust. Helps him
> because he knows and understands C, and reading between the lines he wants
> to target systems that already have C compilers, but not Rust ones.

Maybe someone else can confirm, but I think right now Rust can't cross-
compile; I would hope that this changes after 1.0, as LLVM makes it fairly
easy to do.

I'm also wary of the mentioned productivity gains; I simply do not feel as
confident in the correctness of Nim code I write as I do Rust code I write.
This actually isn't solely due to the enforced move semantics of Rust, but due
to a lot of the comments about Nim in the article linked in the issue; Nim and
Rust surprise me about the same amount, but with Rust the surprise would yield
(sometimes inscrutable) errors from the compiler; Nim was more likely to yield
surprising runtime behavior.

The two languages I use most often are C and Common Lisp.

C has some surprising runtime corners, but the language (at least pre C11,
which I haven't used much yet, so can't comment on) is small enough that I can
keep it all in my own head.

Common Lisp also has some surprising behaviors, and it's a big language, but
this is mitigated by 2 things:

1) Common Lisp allows very concise code; this very often lets me keep all of
the portion of the software I'm working on in my head (versus C, where I keep
all the language in my head)

2) Superior tooling; SLIME lets you very quickly drill down and look at what's
happening. Combined with #1 it is very fast to find where the model of the
code in my head diverges from reality, so most bugs are short lived.

~~~
pcwalton
> Compiling to C shouldn't hurt reliability anymore than compiling to LLVM.

How does Nim deal with the fact that null pointer dereferences, signed integer
overflow, and shifts by more than the width of a type are undefined behavior
in C, and therefore the optimizer can cause memory safety problems if they
occur?

Edit: I just tested myself:

\- Nim emits checks for signed overflow, but they don't seem to optimize to
`jo`. Nim should be emitting these intrinsics where possible:
[http://clang.llvm.org/docs/LanguageExtensions.html#checked-a...](http://clang.llvm.org/docs/LanguageExtensions.html#checked-
arithmetic-builtins)

\- Nim does not emit checks for shifts by more than the width of a type.
Therefore you can get undefined behavior.

\- Nim blindly dereferences null pointers, which is undefined behavior. In
fact, I managed to get clang to optimize out the null pointer check here:

    
    
        echo("Counting to ten: ")
        for i in countup(1, 10):
            var k: ref int = nil
            var l = k[]
            echo($l)
    

In debug mode, this program throws an exception; in release mode, this program
prints zero over and over for me, because clang optimized out the null pointer
dereference per C semantics. I could probably make this program write
arbitrary memory without using any of Nim's unsafe features.

I'm not sure if all three of these are Nim bugs, but they seem like it.
Compiling to C is _tricky_.

~~~
Araq
_Shrug_ , you left out:

\- Conservative GC marking of the stack.

\- No stack overflow check in release mode.

Compiling to C is not tricky, it is horrible and we don't do it for the fun of
it! That said, clang has lots of options to tame C's undefined behaviour.
Guess what, you can enable these too for C code generated by Nim.

~~~
pcwalton
> That said, clang has lots of options to tame C's undefined behaviour. Guess
> what, you can enable these too for C code generated by Nim.

They aren't really designed for performance though. For example, `-fsanitize-
undefined-trap-on-error -fsanitize=null` emits explicit comparisons against
null for every pointer load instead of catching SEGV like (for example) Java
or Go do.

From what you're saying it sounds like maximum performance plus memory safety
isn't a design goal for Nim. That's totally reasonable. It does mean that Nim
and Rust have very different aims, however, and comparisons between the two
need to take this into account.

------
Mikeb85
Having played with both Nim and Rust, I also prefer Nim.

Basically Rust is a slightly more pleasant C++, but still huge, complicated,
things break all the time, and there's no IDE support to help you make sense
of anything. It's probably more robust, maybe faster as things get big, but I
haven't got there yet.

Nim is as easy to write as Python, the toolchain is very easy to set up,
creating Nim interfaces (with documentation) to your favourite C or C++
library is dirt simple (thanks to tools that come with the compiler), and the
language is shockingly fast.

Right now, it's just so much easier to get things done with Nim. Reminds me of
Coffee-Script - because it compiles to another language, you get access to a
million libraries and platforms for free.

~~~
kevin_thibedeau
It's a shame that it doesn't have the mindshare: 27 nim/nimrod questions on
SO, 1548 rust. Still not "notable" enough for a Wikipedia entry. I can see nim
getting stuck lingering on the margin like Objective-C did before OSX.

~~~
Mikeb85
It's possible. I personally just hope that Araq and others keep working on it.
Right now it's quite easy to use without a lot of libraries written in Nim,
because of interoperability.

Besides, I see Nim more as a 'secret weapon' sort of language, or something
that individual programmers use. Rust is more of a 'big idea' language, so it
makes sense a corporation is pushing it.

~~~
def-
> Rust is more of a 'big idea' language, so it makes sense a corporation is
> pushing it.

Araq just announced that Nim has financial backing now: [http://forum.nim-
lang.org/t/870](http://forum.nim-lang.org/t/870)

~~~
Mikeb85
Fantastic. And thanks for that link. I was messing around with some 3D stuff
in Nim, and he's working on 3D C++ stuff too I see...

------
def-
There was a discussion about this on r/rust before:
[https://www.reddit.com/r/rust/comments/2vqy81/author_of_unix...](https://www.reddit.com/r/rust/comments/2vqy81/author_of_unix_in_rust_abandons_rust_in_favor_of/)

My reasons for using Nim can be found in my blog:
[http://hookrace.net/](http://hookrace.net/)

------
jeffreyrogers
Here is the unix kernel (in Nim) that he mentions:
[https://github.com/ckkashyap/nim-xv6](https://github.com/ckkashyap/nim-xv6).
It is based off of the xv6 OS:
[http://pdos.csail.mit.edu/6.828/2014/xv6.html](http://pdos.csail.mit.edu/6.828/2014/xv6.html)

------
keslag
I think the reasoning is sound, but this isn't an indication of either
language being better than the other. Only that it's easier to look under the
hood of Nim. I find Nim easier to read, and then as is with ruby, probably
more productive for prototypes, but I'd like to see more use cases before I
start to move in that direction. It does make me wonder if we'll ever see a
Nim to rust transpiler.

~~~
aikah
2 different languages for 2 different use cases. Seems to me that Nim is more
in league with Go and D while Rust is lower level with some syntactic sugar.
Might be wrong but that's how I feel about these. Anyway it's great to have
new french languages that you can still link to C libs when you need it.

~~~
Mikeb85
I would agree. I don't think they compete directly, the only real similarities
is they both compile to native code, and both are fast.

Nim is more of a Pascal/Python/Coffeescript hybrid with easy interoperability
with C and nice tooling.

------
tokyo1000
What is Nim's community like? I ask because I first learned about Nim a few
weeks ago when some people were chatting about it on Slashdot. One comment
[[http://slashdot.org/comments.pl?sid=6771453&cid=48860921](http://slashdot.org/comments.pl?sid=6771453&cid=48860921)]
quoted from that day's Nim irc logs and well it was a little disturbing. There
was a lot of insulting and name calling going on and it didn't leave a good
impression on me. I don't want to judge the entire Nim community of course but
I was taken aback by those many very negative quotations. I don't think I've
ever seen that kind of animosity in the Rust irc channels I mean.

~~~
codexon
I would say it is unfair to judge any language by their IRC channel unless
they are core members of the group.

In my experience IRC channels being rude is the norm rather than the
exception.

~~~
nnethercote
> In my experience IRC channels being rude is the norm rather than the
> exception.

But it doesn't have to be that way. Rust has a very clear code of conduct
([http://www.rust-lang.org/conduct.html](http://www.rust-
lang.org/conduct.html)) aimed at avoiding exactly that kind of rudeness, and
it has so for a long time. As a result the #rust IRC channel is a very
pleasant place to be.

------
jwecker
We just switched from Rust to Nim for a very large proprietary project I've
been involved with after rejecting proofs of concept in Go and Erlang earlier
in the process. This despite the fact that we had formally decided on Rust,
had tons of code developed in it, and only stumbled upon Nim by complete
accident randomly one day. Even though many large components were already
developed in Rust we have already reached par and have shot ahead of where we
were at. I don't want to diss Rust in any way (or Go for that matter), but I
figure they both have corporate backing and can easily speak for themselves,
whereas Nim does not- so I don't feel too bad offering our somewhat informed
opinion :-)

In a nutshell, Go ended up not really being a systems language and Rust has
the beautiful goal of memory safety and correctness without garbage collection
but in the end feels like it's designed by committee.

Nim, with Araq it's benevolent dictator, has had the opportunity to be highly
opinionated. Much of the time that seems to simply allow projects to either
shoot themselves in the foot or stagnate, but occasionally it produces true
gems- and I really feel like that's what Nim is.

Some aspects of Nim that keep blowing me away more and more:

* Truly allows you to be close to the metal/OS/libraries (most beautiful and simple ffi ever) AND simultaneously allows you to think about the problem at hand in terms of the problem- i.e., high level abstractions and DSL creation like you can do with Ruby (but even cleaner in many ways). I always thought those were two opposite ends of a spectrum and didn't expect a language to even attempt to bridge the chasm- then along comes Nim and not only attempts but succeeds beyond what I ever would have thought possible. To illustrate: it has become my go-to language for quick scripting, faster to whip something together than shell or ruby or perl (and many orders of magnitude faster to run- often even including the automatic compilation the first time)- while also being the fastest, cleanest, safest way to do something low-level, like a mmapped memory-mirrored super-fast circular buffer or cpu-cache optimized lock-free message passing...

* Even though it has C as an intermediate step, it doesn't feel or act anything like C. While not as strong as Rust in this area (I know, Rust goes straight to LLVM's IR instead of C- but the risks are the same)- it generates code that is much more safe (and concise) than writing it straight in C. I know, there are other language that do this well also- but usually by sacrificing the ability to easily and cleanly do system-level coding- like a raw OS system call, for example.

* On a related note, despite feeling more high level than Rust, it can do so much more at a low level. For example, using musl instead of glibc (which at least last time I checked wasn't possible in Rust due to some accidental coupling).

* So fast. While premature optimization is considered the root of all evil, and linear optimization often does not end up adding significant real value, I've been reminded more in the last few weeks than ever before that when speedups are around 2+ orders of magnitude _it is often a complete game changer_- it often allows you to model the problem in a completely new way (don't have a generally understandable example for Nim offhand but take docker vs dual-boot for an extreme non-Nim example- VMs were a mostly linear improvement over dual-booting and other network-based workarounds, but only with linux containers and their orders-of-magnitude "startup" time, snapshotting, etc. etc. was docker able to say "these are so cheap and fast we can assume a single running process per 'image'").

* Even though it's not as formally safe as Rust yet, in practice it feels and acts as safe, without the cognitive overload.

* Rust gets tons of new commits from tons of contributors every day. I worried when looking at Nim that the lower volume of commits meant that the language was less... alive or long-term-tenable. But on closer evaluation I realized that there was literally less that needed changing, and that the changes that seemed to need code to change all over the Rust repository would, given an equivalent scope in Nim, require trivial changes to just one or two files. (for example, Rust's massive [and extremely slow] bootstrapping pipeline and parsing architecture, vs Nim's built in PEG syntax and 5-line loop that keeps compiling itself using the last iterations' outputs until the outputs don't change anymore).

In short:

\- All the simple beauty (IMO) and conciseness of a python-like/pseudocode-
like syntax without all the plumbing and pedantic one-way that comes with
python. In other words, beats python at python's own strengths (not even
mentioning speed or language features etc...)

\- Top-down modeling and DSL affinity like Ruby with less feeling of magic-
e.g., better "grep"-ability and tracing. In fact, the DSLs and up being
cleaner than idiomatic Ruby ones. So beats Ruby IMO at one of Ruby's core
strengths.

\- Seems as safe as Rust with much cleaner syntax and simpler semantics. (this
is something we're actively quantifying at the moment). Easily 10x the
productivity. _Almost_ beats Rust at its core strength.

\- Easiest FFI integration of any language I've worked with, including
possibly C/C++.

\- All the low-level facilities of C/C++ without the undefined-behavior, with
better static checking, much better meta-programming, etc. etc. Beats C/C++ at
their core strength.

\- Utterly intuitive obliteration of autotools/configure/makefile/directives
type toolchains required for system development using C/C++.

\- It's very fast and efficient per-thread garbage-collection (when you want
it) essentially allows it to eat Go and Java's lunch as well.

\- It's a genuinely fun language (for us at least).

I've already been too verbose and am out of time but I should include for some
sense of completeness some current weaknesses of Nim. None of these ended up
being remotely show-stoppers for us, but at least initially we worried about:

* Too much attention to windows (doing _nix and even linux-only development is so easy it has turned out to be a non-issue).

_ Less safety than Rust when doing manual memory management (hasn't been an
issue and we believe unlikely to be an issue in practice).

* Lack of (implied) long-term corporate support (already more stable than some others and community is strong where it counts. Also, no matter how much corporate support they get- Rust will still be more verbose and designed by committee and Go will still fall short of being a true to-the-metal systems programming language and neither will ever have Ruby and Lisp's moldability and endless potential to get out of your way).

* Smaller community/package ecosystem than many others (so easy to do FFI or to even _reimplement something done in C using 1/5 the code_ that it also has turned out to be a non-issue. Now I worry that it's so easy to code that it will have a library-bloat issue like Ruby requiring something like ruby-toolbox)

* "How have I not heard about this before?? Why isn't it bigger? Is something wrong with it?" I start worrying about things like D's standard-library wars or lisp & scheme's utter flexibility combined with poor readability... Nope, just new and only spread through word-of-mouth, like Ruby, Python, and Perl long ago...

[there, once I've written three separate lists in a single comment I can
safely say I've said too much and should get back to work].

~~~
parley
I also apologize beforehand for a long reply. Long posts get long replies.

I can definitely understand this point of view, but I just can't agree. The
parent probably wants his/her claim that Nim seems as memory-safe as Rust to
_not_ be interpreted literally, as a literal interpretation would make the
statement false (by any fair comparison using idiomatic code from both
languages to accomplish the same thing).

What the parent is surely talking about is how it pans out in practice.
Different languages have their different trade-offs here with different
pitfalls, and denying that Nim can crash and burn due to memory management
mistakes would be false. Denying it with respect to Rust would also be false,
due to Rusts optional unsafe features, but the important distinction is how
easy it is to make these mistakes in idiomatic code and what the consequences
will be. Only time will tell, which is why anecdotes are of interest, of
course - both the parents and everyone elses.

However, I find some choices of words to be a bit disingenuous (though
hopefully unintentionally so).

The claim about being able to do "so much more at a low level", like e.g.
being able to switch out libc variants, which allegedly is not possible in
Rust due to accidental coupling. Is this a temporary difference? If so, it may
only be relevant in the short term. I can't answer this question, but it would
be interesting if someone did.

Most importantly: "Even though it's not as formally safe as Rust yet, in
practice it feels and acts as safe, without the cognitive overload." Yet?
Making Nim as formally safe as Rust would require completely changing key
aspects of the language. Feeling as safe is possible, and acting as safe is
possible too...

... until it doesn't anymore, that is, because the team grew (as it always
does, some leave, some join, etc) and the code base ballooned and someone made
a simple memory management mistake _somewhere_ that is now a serious debugging
problem and no code can be eliminated beforehand from the necessary auditing
because the entire code base is vulnerable to these classes of errors.

Memory management errors have a way of resulting in seriously trashed core
dumps, etc, sometimes severely complicating and limiting debugging
possibilities. Where's my stack trace? Oh, we seem to have been executing data
and not code. Where did we come from? Oh, no intelligible stack frames. No
valid return address in the register, etc. I've been there, as I'm sure many
of us have. Memory management errors can lead to complete debugging
nightmares, and that's if they're even reproducible by developers. If they're
only triggered at the customers site due to their unique circumstances, good
luck. Having a deterministic test trigger it and being able to run it through
valgrind until it's solved is the optimal cake walk scenario, but that's not
real life most of the time.

Rust can step quite easily from low-level stuff to high-level features and
meta-programming too, and I feel no comparison is really made by the parent,
only talk of Nims features. The central premise as always for Rust is that it
provides what it can provide while still maintaining memory safety. Rust
without this prerequisite would not be Rust, and the constraints for
everything else flows from it.

The repeated claim of design-by-committee is also not the best one. Having
followed Rusts back-and-forths for years, I have to say I feel the discussion
has been extremely well functioning, and most importantly: The choices have
been very pragmatic within the constraints of preserving the key safety
features of the language.

Personally, having gone through many languages all over the abstraction level
spectrum and specifically having spent quite some time in embedded C/C++, I am
terribly, horribly tired of fatal runtime errors in general and memory
management errors in particular. They can cost so much time to debug and fix
that development time can swoosh past what it would have been in a language
with a type system preventing them in the first place. Your mileage may vary,
of course!

There is something to be said for languages that simply eliminate these
classes of errors compile-time, and that something is actually _a lot_. For
the small programs, tooling, scripts... I can write them in anything. There
are hundreds of choices. That's _not_ what this is about. For the software
that _matters_ , that _ships_ and that _others will expect to work_ , I no
longer have the patience or tolerance for these error classes.

Many languages with such safety guarantees (and Nim is not one of them) have
already existed for a long time, but very few that can be applied to all the
use cases that Rust can. That is what it's about. This is why people are
excited.

Software development is a form of art and a form of engineering, at the same
time. A lot of software doesn't have to be as reliable as space shuttle
firmware, and I'm not claiming it has to, but the general bar could sure as
heck be raised several notches. We know how the world works, and yesterdays
quick hack or proof of concept is todays firmware shipment for use in live
environments. Successful software lives for a long, long time. Software _is_
eating the world, and society is now at its mercy.

Personally, I will sleep so much better knowing that these error classes were
wiped out compile time in 99.?% of the code I shipped to those customers,
while being able to maintain on par performance with the C code it replaced.

These are of course my $0.02, and I hope it didn't come across as combative as
that was definitely not my intention - only passionately conveying my own
perspective. :)

~~~
jwecker
Thanks for the thoughtful response- it didn't come across as combative at all
to me.

The true, provable safety of Rust was what drew me to it as well. I've always
hated having to choose between un-principled memory management (with it's
security and functionality vulnerabilities that can lie dormant for many years
before kicking your butt) and garbage-collection forcing you away from the
metal and removing deterministic reasoning about memory usage, runtime
behavior, and runtime overhead.

I've been going through the academic papers, forerunners, and source-code for
Rust's static memory routines and borrowing semantics. My hope and suspicion
is that it can be added to Nim without core changes to the language like
lifetimes. It's definitely not a guarantee, but with lots of experience in
both languages now I feel very strongly that adding region-based-memory-
management to Nim is possible while adding Nim's clarity, abstractions, and
efficiency to Rust feels impossible.

I agree that at the moment Rust is the only responsible choice right now if
provable memory safety is a primary concern, but I suspect that will change.
In the mean-time, for us anyway, the price was too high in productivity when
we discovered that we could do manual memory management in Nim in very well-
considered isolated places and confidently use Nim's fast, real-time
deterministic per-thread garbage-collection for everything else without a
noticeable performance penalty.

Having said that, I don't think I actually disagree with anything you said (:

~~~
pcwalton
> I've been going through the academic papers, forerunners, and source-code
> for Rust's static memory routines and borrowing semantics. My hope and
> suspicion is that it can be added to Nim without core changes to the
> language like lifetimes. It's definitely not a guarantee, but with lots of
> experience in both languages now I feel very strongly that adding region-
> based-memory-management to Nim is possible while adding Nim's clarity,
> abstractions, and efficiency to Rust feels impossible.

I'm not so sure. The trickiest part of getting memory safety without garbage
collection working is not the lifetimes but the borrow check, which relies on
inherited mutability and, most importantly, _the lack of aliasable mutable
data_. The APIs and libraries of garbage collected imperative languages
invariably depend on aliasable, mutable memory. Consider something as simple
as a tree or graph data structure with mutable nodes. Or consider taking two
mutable references to different indices of an array, or splitting an array
into mutable slices with dynamically computed indices. These are all things
you (presumably) can do today in Nim, and a borrow checker would break them.
The likelihood that the library APIs depend on being able to do it is very
high.

I never say never: you could implement multiple types of references, some GC'd
and some not, and copy the Rust borrowing semantics. But they would be
incompatible with most existing APIs and libraries. I don't think it can be
realistically retrofitted onto a language without breaking most APIs:
aliasable, mutable data is just too common.

Regarding efficiency/performance, what in particular seems impossible to add
to Rust?

------
pkulak
Isn't Nim GCed? How are you going to build a kernel like that?

~~~
chroem-
GC in Nim is highly configurable: you can pause GC or even delay it
indefinitely. Plus there's lots of tweaks you can make to how GC treats your
code using Nim's pragma system. And no, the standard library does not rely on
GC like in D.

~~~
pcwalton
My issue with this approach is that it seems inherently racy in multithreaded
programs. Nim gets around that issue by having a per-thread GC and segfaulting
if you send pointers to other threads (unless you use the Boehm GC, last I
checked)—your only backup seems to be manual memory management—but I don't see
that as a scalable approach for large-scale software with dozens or hundreds
of threads. (Not to mention that not having a thread-safe GC neglects a major
benefit of a GC for lock-free data structures; having to use hazard pointers
if you're a GC'd language seems like a shame.)

------
marijn
Last time I looked at Nim, I was put off by the lack of algebraic data types
and pattern matching. I still can't find anything about them in the manual. Am
I missing something, or is this something it just doesn't have?

~~~
def-
Instead of ADTs there are object variants in Nim: [http://nim-
lang.org/manual.html#object-variants](http://nim-lang.org/manual.html#object-
variants)

With ADTs:

    
    
      data Shape a =
        Rectangle
          { x :: Float
          , y :: Float
          , width :: Float
          , height :: Float
          }
        | Circle
          { x :: Float
          , y :: Float
          , radius :: Float
          }
    

With Object Variants, note that we don't have to repeat x and y:

    
    
      type
        ShapeKind = enum Rectangle, Circle
      
        Shape = object
          x, y: float
      
          case kind: ShapeKind
          of Rectangle: width, height: float
          of Circle: radius: float
    

The pattern matching in Nim is quite limited. Something like this works:

    
    
      let (x, y) = getCoordinates()
    

But Nim doesn't have (x,y) = (y,x) for example, instead swap can be used.

You could implement your own pattern matching using metaprogramming:
[http://www.drdobbs.com/open-source/nimrod-a-new-systems-
prog...](http://www.drdobbs.com/open-source/nimrod-a-new-systems-programming-
languag/240165321)

------
thiht
Maybe it'll be renamed "Nimix", that's a great name!

------
bhouston
An idividual drawn to novel immature languages switches their interest as
language matures.

