
On Rust and Nim - _qc3o
https://andreaferretti.github.io/on-rust-and-nim/
======
shadowmint
Pretty fair commentary.

Rust certainly isn't one of those languages where you can just pick it up,
play with it for a day implementing an algorithm in it to get the feel of it
and learn 'the complicated stuff' later.

Other languages let you get away with that quick start style; you can write a
lot of python before you need to write a plugin, and a lot of c# before you
start using unsafe code, etc.

Rust doesn't afford you that luxury.

Lifetimes, mutability and single ownership are BAM, right in your face from
the start.

It probably puts a few people off... but hey, you know the analogy about tools
and toolboxes.

Rust is for writing fast, secure, cross platform code. There's nothing else
out there that offers the same; it's not a case of use Rust or Nim, or C++;
rust is _literally_ the only language that offers these features right now.

You can certainly write code that happens to be secure, fast and cross
platform (eg. in C++), and if you don't need those features (or dont care),
you're almost certainly better off picking a different language (like Go or
Nim) that are 'fast enough' and 'secure enough', and don't restrict you in the
same way Rust does, or something far more productive (like python or
javascript) if all you need to do is smash out a product.

That's perfectly ok.

We don't need a language which is everything for everyone all at the same
time.

Rust is very good at doing what it does; and it's the first time C++ has had a
real challenger. I, for one, am really looking forward to the dynamics between
the two crowds going forwards.

~~~
tptacek
_Rust is for writing fast, secure, cross platform code. There 's nothing else
out there that offers the same._

That's a weird way to put it. There are a _lot_ of languages that offer the
same (weak) security protections Rust does, and a _lot_ of cross-platform
languages, and a _lot_ of fast languages, and lots of combinations of those
attributes.

What I think you mean to say is that there aren't a lot of combinations _that
don 't have a GC runtime_.

~~~
pcwalton
Based on our analysis of security bugs in large C++ codebases, I wouldn't
characterize memory safety as "weak"—the vast majority of critical RCE bugs in
C++ codebases are due to memory safety issues.

~~~
tptacek
Most modern code isn't written in C/C++, and yet regardless of the mainstream
language it's written in, vulnerability hunters still find game-over issues.
Eliminating opportunities for memory corruption is an unalloyed good, but
let's be candid about how much of the whole software security problem that
solves.

~~~
pcwalton
Memory safety is a defense against certain classes of vulnerabilities, no more
and no less than that. I've always been careful never to claim that memory
safety eliminates all security vulnerabilities, or that people won't find
game-over attacks against apps written in Rust. Still, I don't agree with the
characterization of memory safety as a "weak" defense—it's a defense against
what are far and away the most common classes of critical vulnerabilities that
we see in C and C++ programs.

I agree with you that there's nothing special from a security point of view
about Rust if you're, say, a Python, or Java programmer (though the non-
security-related safety features—for instance, data race freedom—may be
interesting). Whether Rust is a security advance for you really depends on
your starting point and what you consider to be non-negotiable. If you're a
Java programmer for whom memory safety is non-negotiable, Rust isn't a
security advance, but could be a performance improvement. If you're a C++
programmer (like us in the browser space) for whom C++-level performance is
non-negotiable, then Rust isn't going to be much of a performance improvement,
but it is a security advance relative to what we had to work with before.
Basically it's about _eliminating the tradeoff_ between performance and a
class of security problems—whether that's a security advance will depend on
where you started from.

------
alextgordon
I too have suffered Rust's religiosity on the floating point issue.

In Python, given an array xs = [3.1, 1.2, 4.3, 2.2], I can write

    
    
        xs.sort()
    

and get [1.2, 2.2, 3.1, 4.4]

In Haskell

    
    
        sort xs
    

In Swift

    
    
        sort(&xs)
    

In Rust you have to spew this monstrosity

    
    
        xs.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Less))
    

The Rust position appears to be that sorting an array of floats is
unreasonable and so you must be "punished" by not being allowed to use the
built-in .sort() function.

~~~
heinrich5991
What is the expected result if you have NaN in your list?

~~~
alextgordon
I don't have NaNs in my list. That's an invariant I would be happy to express
in the type system.

~~~
steveklabnik
A 'newtype wrapper' has your back in that situation, which lets you do exactly
that.

~~~
sanderjd
Yep, and that type _can_ have a total order and work with the `sort` method
with no further ceremony. That actually might be a nice type to have in the
standard library. It seems like it would be widely useful, but I'm not sure
where it would fit in on cargo.

~~~
twic
I had a crack at this. This is about the third Rust program i've ever written,
so it's probably chock full of noob mistakes:

    
    
        #![feature(std_misc)]
        mod natural {
          use std::num::Float;
          use std::iter::IntoIterator;
          use std::iter::FromIterator;
          use std::cmp::Ord;
          use std::cmp::Ordering;
    
          #[derive(PartialEq, PartialOrd, Debug)]
          pub struct Natural(f64);
    
          impl Natural {
            pub fn new(value: f64) -> Option<Natural> {
              match value {
                x if Float::is_nan(x) => None,
                _ => Some(Natural(value))
              }
            }
            pub fn new_all<'a, A: IntoIterator<Item=&'a f64>, B: FromIterator<Natural>>(values: A) -> B {
              let b: B = values.into_iter().map(|f| Natural::new(*f).unwrap()).collect();
              b
            }
          }
    
          impl Eq for Natural {
          }
    
          impl Ord for Natural {
            fn cmp(&self, other: &Self) -> Ordering {
              self.partial_cmp(other).unwrap()
            }
          }
        }
    
        use natural::Natural;
    
        fn main() {
          let fs = [3.0, 1.0, 1.0];
          let mut xs: Vec<Natural> = Natural::new_all(&fs);
          println!("before = {:?}", xs);
          xs.sort();
          println!("after = {:?}", xs);
        }
    

In particular, the assignment of the return value of new_all to a local is
ugly, but i couldn't figure out how to please the type checker without it.

~~~
sanderjd
Neat! My version with some mostly superficial changes[0].

Note that we haven't actually removed the panic in the `Ord` implementation!
Which is because we've eliminated what we believe is the source of the
ordering uncertainty (the NaN), but the type system still doesn't know that.

~~~
sanderjd
Whoops, didn't post the link: [http://goo.gl/7AZxa6](http://goo.gl/7AZxa6)

------
dodyg
I find it amazing (for good) that Nim, a language that was made in obscurity
by a handful developers managed to be compared to a high visibility language
backed by many organizations and people.

~~~
logophobia
Rust is trying to do something far more complicated. Memory-safety enforced by
the compiler is a big deal, but it remains to be seen if it's practical. I am
really looking forward if rust can make it work, zero-cost memory-safety would
really be something amazing to have in a language.

Nim seems to be a nice, low-level, gc-ed and practical language. It doesn't do
anything radical, it's pretty small and it does what's already there right. It
could become big.

This kinda reminds of linux vs minix. Doesn't mean it will play out the same
way, but still.

Rust feels a bit difficult to get into right now, but if they smooth over the
impractical bits, it could become the systems language in 10 years. Nimrod
feels like a faster version of python or ruby, something a lot of people would
like to have.

~~~
moe
_Nimrod feels like a faster version of python or ruby, something a lot of
people would like to have._

Hell yes!

Nim really looks like it might have the potential to become the "faster Ruby"
(or faster Python) that many of us are waiting for.

For all the progress in academic (Rust, Haskell) and special purpose (Go,
Dart) languages, a new iteration on the "general purpose workhorse" is more
than overdue.

~~~
twic
I hadn't made this connection before, but now i see it put like this, i'm
interested in Nim.

The only language to have entered this "faster, statically typed, and
generally less surprising Ruby" niche so far is Go. Other languages which are
faster, safer, and saner than either Ruby or Go come with various showstopping
problem: Java has too much baggage, Scala and Rust are too difficult, Clojure
is too scary-looking, etc. Go, despite being fairly mediocre, it combines some
concrete advantages over Ruby with a very low barrier to entry.

Nim, though, looks like it should do this even better. It apparently has the
same straightforwardness as Go, similar performance, but with even less
verbosity, a more powerful (but not scary!) type system, and comprehensively
more modern facilities.

I don't know if Nim has some equivalent to Goroutines, but i think Goroutines
are overblown anyway. Go isn't really all that great for concurrency, and the
people i know who are using Go aren't using it for concurrency.

I believe that the decisive fronts will be mindshare and tooling.

I have no idea how Nim can build mindshare on the same scale as Go; i don't
know if the current level of grassroots interest can grow, or if it needs a
corporate backer like Google, a celebrity figurehead like Rob Pike, or some
technical hook like Goroutines. Perhaps someone will build something amazing
in it, and become a poster child.

Tooling is clearer. Go has some simple, well-liked tooling in the box: go fmt
for formatting, go vet for linting, go fix for version upgrades, and go get
for dependency management. It also has a bit of a weird story around compiling
and linking, but it all works in practice. For Nim to overtake Go, it will
need an equally good or better story about all these things. Fortunately, this
doesn't seem all that hard; the most important area is, IMHO, dependency
management and building complex projects, and Go's tools are pretty poor here.
go get is simple, but the lack of versioning is a huge hole. Maybe someone
should just write a Gradle plugin for Nim?

~~~
wtf_is_up
One thing people never seem to mention is how dead simple it is to cross-
compile Go code. For me to consider Nim or Rust, I need to know that I can do
something as simple as GOARCH=arm GOOS=linux go build.

~~~
twic
Cross-compilation looks pretty simple in Nim:

[http://nim-lang.org/nimc.html#cross-compilation](http://nim-
lang.org/nimc.html#cross-compilation)

Since Nim uses C as an intermediate representation, cross-compilation support
should be as good as your C compiler's.

Rust's cross-compilation seems to not be that great at the moment. Although i
believe that's because it hasn't been done yet, rather than having been done
badly.

~~~
steveklabnik
Rust's cross-compilation works, but it's not as easy as it could be. One issue
is that you need a cross-compiled version of the standard library lying
around.

~~~
__david__
Isn't that an issue with every compiled language? I remember always having to
cross-compile newlib when I set cross-compiled gcc environments.

~~~
Matthias247
For Go you don't. I found it quite awesome when I could cross-compile
something for my raspberry-pi from a Windows PC.

~~~
__david__
Does this mean they ship with the cross compiled libraries installed?

------
Fede_V
The killer feature of Nim is its amazing syntax. If you know Python, Nim will
instantly feel very, very familiar.

Rust looks incredibly useful to do systems level programming, and guaranteed
pointer safety without GC costs is amazing, but it takes me much longer to
figure out exactly what's happening in the code.

~~~
Tharkun
Surely what is or isn't amazing syntax depends entirely on your preferences
and personal experiences. I can't stand the Python syntax. Significant
whitespace is an instant turnoff for me. For me, figuring out Rust is a lot
easier than figuring out Nim, because that's what I'm used to.

~~~
Ygg2
I second this notion. Thing I hate about Python is that it relies on invisible
characters so two identically looking pieces of code aren't.

~~~
Demiurge
This is something I thought before I actually used python. In practice, two
identically looking pieces of code ARE. Yes, you can mix tabs and spaces, but
not in the same block. The moment there is ambiguity the interpreter errors
out. So, in practice this is never a problem, your text editor should not be
switching on you randomly and the official style guide strongly asks you to
use 4space tabs. Simply follow the official guidelines and be able to
configure your text editor, and you will never think about this again if
you're actually using python.

~~~
Ygg2
> Yes, you can mix tabs and spaces, but not in the same block.

Assuming of course all code contributors memorized the official style
guidelines. And that they are responsive to such changes. I've had one similar
change, reverted three times during my uni project, within span of days.

~~~
andybak
I have never found significant whitespace to be a significant problem in
practice.

Very few people use tabs. 4 spaces is the norm.

I just don't get the fuss surrounding this issue. It just works and the
benefits far outweigh any cost - real or imagined.

~~~
oldmanjay
The so-called benefits always boil down to "this suits my preferences" which
is frankly not a compelling argument in any way.

Edit: autocorrect ran rampant

~~~
andybak
> The so-called benefits always boil down to "this suits my preferences"

The of significant white-space are:

1\. Reduction of visual noise and improved readability. I find this
uncontroversial. Your eye parses code by indentation, not by curly-braces. I
could remove the curlies from javasscript and if the indentation was correct,
you'd be able to follow it.

2\. Reduction of cognitive load. Instead of giving me two jobs to do: indent
correctly and match braces - Python only gives me one of those jobs.

These are real benefits that I experience whenever I work with Python. There
are also genuine costs* but I find the balance to be very much in favour of
significant whitespace .

* restrictions on possible choices of syntax being the only one I think actually affects me in anything other than a theoretical sense.

~~~
Ygg2
Eh, most languages and tools nowadays reduce the risk of indenting incorrectly
or not matching braces to near zero, so in practice there is little cognitive
load either way.

Only serious cognitive load for me writing code is whether to add new line at
80 or less chars. That's about it.

------
aphexairlines
Looks like comparing apples and oranges. If Nim has a GC it would be more
instructive to compare it with another garbage-collected systems language like
OCaml.

~~~
z92
What's wrong with comparing a GC language with non-GC language?

~~~
lifthrasiir
Wildly different goals, given that there should be some interesting reason to
avoid GC nowadays.

~~~
rdtsc
How are they "wildly" different. I can see different but "whildly" really?

GC is an implementation detail with some performance characteristics. Nim can
turn its GC off. It can do a soft-realtime GC behavior where you limit its
maximum time slice.

~~~
tomjakubowski
> Nim can turn its GC off.

At the expense of losing memory safety.

------
halosghost
Okay, so I'm having one problem with nim. Disabling all unsigned arithmetic
by-default. The logic is actually fairly sound (it's probably, generally,
harder to overflow a signed than an unsigned), but nim doesn't compile to
object code; it transpiles to C and then C compiles to object/machine code.

Edit: it's obviously much harder to overflow an unsigned than a signed; in the
sentence above, I was thinking particularly of underflow (which is what the
nim devs reference as their logic for disabling unsigned arithmetic by-
default).

The problem here is that unsigned arithmetic, though much easier to hit
underflow, is fully defined in C where signed {under,over}flow is UB. As a
result, if you manage to hit this case in nim, you're now going to hit UB by-
default. This seems crazy to me. Am I missing something?

~~~
def-
While unsigned arithmetic is not "enabled by-default", a simple `import
unsigned` and you have it.

If you want the language to handle overflows for you, you can enable runtime
overflow checks for your whole code or any specific part of it.

Otherwise, if you opt for no runtime checks and release builds with all
optimizations, you indeed go into the same undefined behaviour territority as
in C, so you'd have to prevent overflows before they happen.

~~~
halosghost
I understand that it is available simply; I was questioning the logic of
having it disabled by-default. Having the ability to do run-time checks for
{over,under}flow does seem to make this issue a little better but doesn't
explain the logic of having the language prefer UB by-default.

Yes, C does this too (integers are signed by-default), but if I'm shopping for
a language that abstracts C, I'm probably looking for improvments over C's
defaults.

------
throawai
Keyle [dead]:

I love nim. I've been using it non-stop since I've learnt the ropes. The only
thing I'd wish was better results when googling for things. "nim" is just a
very common word it appears. The site itself is a wealth of knowledge. I can
relate to the comment of "feeling it's too big". Sadly the doco is not newbie
friendly for some part (hi there, async). I've had no issues with the compiler
but be sure to always use the devel branch. Things move fast in nim. \-----

~~~
dom96
I'm curious why the post was deleted. Can mods shed some light on this?

~~~
steveklabnik
They are shadow banned.

~~~
keyle
What is shadow banned and why am I?

------
Animats
That's a good commentary. As for Nim, do we really need another unsafe
language with a part-time GC? If you can tolerate a GC, there are a lot of
good language options.

I have some issues with Rust's verbosity, especially in the error handling
area. I recently bugged the Rust crowd into changing their overly complicated
replacement for C's "argc/argv" approach to command line parameters.[1] They
listened. The Rust crowd is trying hard in a difficult area and succeeding.

In C and C++, lifetimes and mutability are in your face from the start. It's
just that the compiler doesn't help you with them. For years, I've been saying
that the three big problems with C/C++ are "How big is it? Who owns it? Who
locks it?". C gives no help with any of those. C++ addresses "how big", but
it's not airtight, and C++14 tries to address "Who owns it", but only for new
code, and it's not airtight. Rust aggressively deals with all three problems.

I can understand the unhappiness with Rust, though. It's not a comfortable
language for many modern programmers, especially ones who've never done any
constrained form of engineering. For them, I'd suggest Go and Python. Go can
do pretty much anything you need to do on a web server, and fast. That's why
Google created it. So can Python, but more slowly. Both are memory safe (well,
Go isn't in multi thread mode.)

Understanding the Rust mindset can be hard. That's a documentation problem.
The "Rust for Dummies" book has not yet been written. The Rust tutorial
glosses over the hard issues, and the Rust reference is written for people who
are into language design, compiler design, and theory. Until recently the
language design had so much churn that most of the Rust material on the web is
out of date. In another year, there will probably be a decent Rust book.

With Rust, you need a plan for who's going to own what before you start. Then
you just have to explain that plan to the borrow checker. For a complex,
mutable data structure, such as a DOM or a GUI's collection of interconnected
widgets, this may take design work and design documents. If you plow ahead
without thinking through who owns what, including in the error cases, you'll
get Rust compile time errors. You would have hit trouble in C or C++ too, but
it would have been in the form of a memory leak, crash, or security hole. Now
you have to fix it up front.

There are performance wins in this. Someone recently commented on HN that
they'd discovered that typing into a dialog box produced some insane number
(thousands) of allocation events per keystroke. That was partly because, at
many places in the C++ code, a c_str was being copied into a fresh String
object. That's a consequence of being afraid to borrow a reference to a string
you don't own, for fear of creating a bug. It's safer to make a copy. In Rust,
the compiler will tell you if you can do that safely. If you need to make a
copy, you can, but if you just take a reference and Rust allows it, the code
is good. Big step forward.

[1] [https://github.com/rust-
lang/rust/pull/21787#issuecomment-73...](https://github.com/rust-
lang/rust/pull/21787#issuecomment-73161760)

~~~
steveklabnik
> The Rust tutorial glosses over the hard issues,

I'm not disagreeing, this is more of a survey kind of question, but which
issues are the hard ones, to you?

~~~
Animats
It doesn't go into how structures and ownership interact. It's possible to
create a tree with all elements single-ownership. The relationship between
structs, enums with data, recursive enums with data, and ownership isn't
discussed. Yet this is fundamental to doing any complex data structure.

~~~
steveklabnik
Awesome, thanks. I should be addressing that in the future.

------
tmerr
>>the whole language is verbose: compare these 10 lines
([https://github.com/andreaferretti/kmeans/blob/935b8966d4fe0d...](https://github.com/andreaferretti/kmeans/blob/935b8966d4fe0d4854d3d69ec0fbfb4dd69a3fd1/rust/src/point/mod.rs#L30-L39))
with this single line
([https://github.com/andreaferretti/kmeans/blob/master/nim/alg...](https://github.com/andreaferretti/kmeans/blob/master/nim/algo.nim#L10))

The Nim code's more concise in this case but the Rust code can be more clearly
written:

    
    
      impl Add for Point {
        type Output = Point;
      
        fn add(self, other: Point) -> Point {
          Point(self.0 + other.0, self.1 + other.1)
        }
      }
    

The "type Output = Point" line isn't boilerplate, it makes it possible to
define the result of an addition as something other than a Point. (Off of the
top of my head I can't think of any use cases, but I'm happy with the
capability personally).

~~~
Coding_Cat
For addition I can't either, but you could use it to overload multiplication
of two (mathematical) vectors to a dot-product

    
    
      impl Mul for Vec2{
        type Output = double;
        fn mul(self, other: Vec2) -> Double {
          self.0*other.0+self.1*other.1)
        }
      }
    

Although I must admit I'm not (yet) sure why you need to explicitly state the
output _and_ define it for the function. But I have only just started with
Rust.

~~~
Ygg2
> Although I must admit I'm not (yet) sure why you need to explicitly state
> the output and define it for the function. But I have only just started with
> Rust.

I generally assume it's because it can't infer the associated type from
function signature, for now.

------
keyle
I love nim. I've been using it non-stop since I've learnt the ropes. The only
thing I'd wish was better results when googling for things. "nim" is just a
very common word it appears. The site itself is a wealth of knowledge. I can
relate to the comment of "feeling it's too big". Sadly the doco is not newbie
friendly for some part (hi there, async). I've had no issues with the compiler
but be sure to always use the devel branch. Things move fast in nim.

------
thechao
> but adding a map function on Vector would have not prevented this more
> sophisticated use.

This was questioned, long ago, by Todd Veldhuizen in his Parsimony Principle
paper[1]. The long-and-short of which is: do it!

[1] [http://arxiv.org/abs/0707.4166](http://arxiv.org/abs/0707.4166)

------
fiatjaf
Do you create a new repository on GitHub for everything you want to write?

------
arbre
I would be curious on how Go would compare to these two on that example.

