Go and Rust offer GC. Rust allows you to choose when you use it. Rust also makes it explicit when you're allocating memory whatsoever.
Go and Rust offer M:N threading. Rust provides separate heaps per "task" and a data ownership system which means you never end up sharing state between "tasks" (ala Erlang). Go only provides a global shared heap. This means Rust can prevent a class of bugs (concurrent mutation of shared state) which Go can't. It also means Rust's GC is easier to run in parallel, since state is isolated to particular tasks so you can GC a task while it's idle ignorant of the rest of what's going on in the system.
Go and Rust both offer structural typing (like duck typing with static typechecking). Rust also implements Hindley-Milner, which is the same type inference algorithm used in Haskell. So Rust provides Go-like typing when you want it, and a "real" type system when you need it.
Rust is immutable by default. I think the benefits of immutable state go without saying, but Rust also lets you opt into mutable state if you want it.
Rust is memory-safe by default, meaning that mistakes made by programmers won't corrupt memory or cause the program to crash. However, Rust also offers an unsafe mode that provides C-like memory access, making Rust suitable for things like kernel development, bootloaders, microcontrollers, and other device firmware that still needs to do C-like things in order to function.
Rust can be used for "bare metal" low-level development, to the point that you can boot directly to Rust without ever starting its runtime, and even use Rust directly on microcontrollers. See "Rust on bare metal" (lolpun): https://mail.mozilla.org/pipermail/rust-dev/2013-July/004841...
Finally, Rust provides a task linking/fault-tolerance model similar to Erlang where if one task exits, tasks it's linked to are automatically notified about an exit event and can take action accordingly.
Rust is a weird platypus of a language with a lot of the features you see in fancy functional languages but working in more or less the same realm as C/C++. I've heard it described as "if C++, ML, and Erlang had a kid"
> Rust provides separate heaps per "task" and a data ownership system which means you never end up sharing state between "tasks" (ala Erlang).
Nitpick: This isn't actually true. Rather Rust ensures at compile time that if you share memory you can't race on it (either you share only immutable data, or you take a lock whenever you mutate). This is one of the things that makes Rust unique among industry languages: you get shared memory and data race freedom.
Technically, you also get shared memory in Erlang--it's just table-structured rather than a block of raw bytes. I've never really come across a situation in Erlang, that calls for shared memory in other languages, that sharing an ETS table and passing messages about events on it doesn't solve more cleanly.
I think that makes sense. Shared memory is mainly a performance optimization. Erlang is not known to have C++-like speed, so if it is fast enough to do in Erlang, it probably is fast enough so that shared memory is not needed.
Isn't Rust moving away from M:N threading and going to the "normal" model of each concurrent thing running in its own thread? (I forget the correct term for that, 1:N threading or something like that?) I'm not sure if they're there yet, but I remember reading they were planning on doing that.
EDIT: Gack, ignore this. Missed that another reply hit the 1:1 threading point. (only read the bit where they were talking about the @-pointer going away)
@-pointers, right now, are retain-counted. But that's just a temporary implementation detail. The current direction is to remove @-pointers entirely and implement both retain-counting and true garbage collection in the standard libraries.
And getting rid of N:M threading and returning to 1:1 seems rather unlikely to happen. More likely is simply work to ensure that Rust can run well under both N:M and 1:1 threading models.
> Rust doesnt really offer GC, the @pointers are more like ARC with cycle detection, and are behind a featre flag now.
@ is being removed, but GC will still be there - implemented properly as a library with language hooks (via traits) to make it nice and safe to work with. You will be able to create your own memory management strategies using those traits.
Last I had heard they were going to try and provide both M:N and 1:1 implementations and using whichever was more appropriate for the platform. I could be wrong about that, though, it's a hazy memory from a lengthy mailing list discussion.
FWIW, linked failure was recently removed from Rust. I believe the current plan is to reintroduce it only on channel boundaries (so failure propagates across channels).
I read about this in the mailing list, but never having relied on linked failure in the past I wasn't sure what the implications were. Could someone give an example of when this would've been used before and how you might accomplish the same in its absence?
With linked task failure, this program here would cause the main task to fail, which results in the entire program aborting (once all tasks have finished failing):
fn main() {
// we can't introduce synchronization for this example, so print repeatedly on the main task and check the output for ordering
do spawn {
fail!("child task failure");
}
for i in range(0u, 50) {
println!("print line {}", i);
}
}
Without linked task failure, the spawned task will fail, but the main task will continue on.
If you want to reintroduce linked task failure with current master, you can do so explicitly using `std::task::TaskBuilder.future_result()`. It's a bit awkward though. And you can probably do better than what I have here, it's just a quick sample:
fn main() {
let mut builder = std::task::task();
let future = builder.future_result();
do builder.spawn() {
fail!("child task failure");
}
// check for failure before each of our prints
for i in range(0u, 50) {
if future.peek() && future.recv().is_err() {
fail!("parent task failing because of child failure");
}
println!("print line {}", i);
}
// and block on the result in the end
if future.try_recv().map_default(false, |r| r.is_err()) {
fail!("parent task failing because of child failure");
}
}
Really, really enjoyed reading this. I've not looked at rust in around a year, and that was before I got into erlang. Knowing how it deals with heap and "tasks" makes me super happy. Probably my next language?
Go and Rust offer GC. Rust allows you to choose when you use it. Rust also makes it explicit when you're allocating memory whatsoever.
Go and Rust offer M:N threading. Rust provides separate heaps per "task" and a data ownership system which means you never end up sharing state between "tasks" (ala Erlang). Go only provides a global shared heap. This means Rust can prevent a class of bugs (concurrent mutation of shared state) which Go can't. It also means Rust's GC is easier to run in parallel, since state is isolated to particular tasks so you can GC a task while it's idle ignorant of the rest of what's going on in the system.
Go and Rust both offer structural typing (like duck typing with static typechecking). Rust also implements Hindley-Milner, which is the same type inference algorithm used in Haskell. So Rust provides Go-like typing when you want it, and a "real" type system when you need it.
Rust is immutable by default. I think the benefits of immutable state go without saying, but Rust also lets you opt into mutable state if you want it.
Rust is memory-safe by default, meaning that mistakes made by programmers won't corrupt memory or cause the program to crash. However, Rust also offers an unsafe mode that provides C-like memory access, making Rust suitable for things like kernel development, bootloaders, microcontrollers, and other device firmware that still needs to do C-like things in order to function.
Rust can be used for "bare metal" low-level development, to the point that you can boot directly to Rust without ever starting its runtime, and even use Rust directly on microcontrollers. See "Rust on bare metal" (lolpun): https://mail.mozilla.org/pipermail/rust-dev/2013-July/004841...
Finally, Rust provides a task linking/fault-tolerance model similar to Erlang where if one task exits, tasks it's linked to are automatically notified about an exit event and can take action accordingly.
Rust is a weird platypus of a language with a lot of the features you see in fancy functional languages but working in more or less the same realm as C/C++. I've heard it described as "if C++, ML, and Erlang had a kid"