Hacker News new | ask | show | jobs
by kbenson 3884 days ago
As someone who's been following the hype, but not really jumped on yet, the difference (whether real or imagined) seems to be that rust is trying to do something new, and D looks to be (and I've seen it aggressively marketed as) C++ with some better choices and cool features. Personally, I'm not really interested in C++, but I am interested in getting for familiar with a systems language, so rust interests me.
1 comments

From what I understand, the only new thing Rust is contributing is managed lifetimes. Everything else is, like in D, just borrowed from other languages and put together.

That said, I've mostly heard that lifetimes are still too young to be worth the trouble. So the one new thing Rust does bring to the table isn't really ready yet.

I think of it like this: If I'm going to manually be managing memory, I might as well get some real gain for that effort. Provably correct memory management through an ownership system sounds like it might be worth the effort, especially if it might also help in situations with threading. I've had reason to use threads quite a bit in the past.
> That said, I've mostly heard that lifetimes are still too young to be worth the trouble. So the one new thing Rust does bring to the table isn't really ready yet.

Not in my experience. I wrote tons of safe Rust every day and I really enjoy not having to worry about difficult-to-debug crashes, undefined behavior, and security problems.

> just borrowed from other languages and put together.

Most of these things are new to the systems space, though. Things like algebraic data types, etc. Also usable affine types.

> lifetimes are still too young to be worth the trouble.

I've heard pretty much the opposite. Firstly, nobody who uses Rust calls the feature "lifetimes" (it's usually called borrowing or borrow checking). That's the name of the syntax, and while the syntax is new and somewhat confusing it doesn't pop up that much thanks to elision.

Borrowing has a learning curve, yes. But really, it's an equivalent set of principles to what you would do in C++ to keep your pointers safe. The difference is that you can write small C++ codebases without worrying about "C++ borrowing", whereas you need to think about these things off the bat in Rust.

> it's an equivalent set of principles to what you would do in C++ to keep your pointers safe.

The things you can prove statically with the borrow checker are a subset of the things that wont trigger UB in C++.

That doesn't contradict what I said.

My point was: If you're working on a large C++ codebase you will have to reason roughly about who owns what and where your pointery data is coming from. IME it amounts to (roughly) the same thing as Rust's borrowck rules, just that Rust forces you to think about this a priori, in a clean framework with clear rules, instead of wibbly wobbly, pointy-wointy, ... stuff.

The things you feel safe to do in Rust are often a superset of the things you feel safe to do in C++. Most large codebases often use lots of shared_ptr or equivalent, whereas in Rust Rc and RefCell are broken out only when necessary (when the compiler tells us there's no way to do it the borrow way).

So while C++ might let you write a larger variety of non-UB patterns in principle, Rust will let you write more non-UB patterns in practice. You feel safe playing fast and loose with the references, and dancing near the line of safe behavior since the compiler ensures you never cross it. I have a post illustrating an example of this here: http://manishearth.github.io/blog/2015/05/03/where-rust-real...

And, of course, if you really want to express one of those "things C++ lets you do" wrt pointers, you can always break out `unsafe`.

Your comment has the structure of a well reasoned argument, but it's predicated on falsehoods.

Most large C++ projects do not unnecessarily use reference counting.

Most C++ programmers do not fear using references or pointers because of invalidation.

The Rust compiler does not tell you when it's impossible to satisfy your problem using borrows. In fact it often has false positives requiring borrow gymnastics because it is too primitive.

And your last line is a complete reversal of your previous attitude: Suddenly you are focusing on theory while ignoring that in practice you can not usually break out `unsafe` to solve your borrow problems. In fact it's Rust, not C++, that is more likely to reach for more heavyweight abstractions than necessary because it's not practical to litter unsafe throughout your codebase.

> Most C++ programmers do not fear using references or pointers because of invalidation.

Compared to Rust? I doubt that. Like I said, Rust lets you dance near the line.

This is from experience in various large C++ codebases. I'm not saying people use refcounting a lot, I'm saying it gets used more than Rust.

YMMV though, so it does boil down to a matter of different experiences here. We'll probably have to agree to disagree.

> In fact it often has false positives requiring borrow gymnastics because it is too primitive.

Not really. Aside from non-lexical borrows and a couple other nice-to-have things (but not necessary), the borrow checker is pretty precise for what it tries to prove.

One might argue that the guarantees Rust tries to maintain (one writer or multiple readers for a piece of data) are too primitive. I don't think that's true. Doing a context-sensitive/flow-sensitive analysis might lead to more patterns being allowed but it's hard to scope guarantees when the analysis is interprocedural -- stopping at function boundaries makes sense to me.

> The Rust compiler does not tell you when it's impossible to satisfy your problem using borrows.

It sort of does. If you try to introduce borrows and listen to the suggestions the compiler gives you, and eventually end up nowhere, it's probably not possible to do it that way. It's not perfect, but it's good enough. And it's immune to further changes -- you don't need to design your pointer usage so that it's future-proof; design it however you want, and if a future refactoring introduces a possible use-after-free, fix the compile error.

> And your last line is a complete reversal of your previous attitude: Suddenly you are focusing on theory while ignoring that in practice you can not usually break out `unsafe` to solve your borrow problems.

No, it's not, I'm just pointing out the equivalence. My point was that "The things you can prove statically with the borrow checker are a subset of the things that wont trigger UB in C++." is irrelevant for two reasons -- (a) in practice IMO the things Rust makes you feel safe to do is a superset of what C++ lets you feel safe to do, and (b) if we're going to talk about "all possible things C++ theoretically allows you to do", then you should include unsafe -- you were comparing "safe Rust" with "all C++", which is unfair here, since the entities to be compared for what you're theoretically allowed to do are "all Rust" and "all C++". I focused on a different flaw in your argument, so of course the focus changed. I'm not saying you should use unsafe a lot, I'm saying "what C++ lets you do is equivalent to using unsafe a bit more often in Rust". I don't endorse it, but if you consider "C++ lets you do so many things Rust doesn't" to be a plus point of C++ (I don't), then you should at least be comparing the right things and allowing yourself to use unsafe in Rust too.

> more heavyweight abstractions than necessary

Examples?

D's new things have been more accidental than by design... and kinda subtle too. Like the compile time reflection. I'm sure someone else has done it somewhere before, but D makes it really easy.

  > lifetimes are still too young
While some stuff is new, the core of the idea goes back to Cyclone in 2001.