"This blog post brought to you by the 'how many times can you say 'fearless concurrency' and keep a straight face' cabal.
"Seriously though, I now appreciate that term a lot more. One thing that cropped up in the review of this post was that I didn't have examples of bugs Rust prevented. Because I couldn't think of any concrete ones. Because Rust's safety doesn't work that way, it prevents your concurrency bugs before you realize you had them, by making sure you don't paint yourself into a corner. 'Fearless concurrency' really is the best way of putting this; the benefit was not that it prevented concrete bugs, but that it let us fearlessly and aggressively write code knowing that it would be concurrency bug free."
https://www.reddit.com/r/rust/comments/7cwpbq/fearless_concu...
The Rayon library is also lovely for data parallelism.
Sometimes, I think, "Rust is basically a nicer C++ with the obvious foot guns removed and great tooling", but then there are those moments where I'm just blown away by what a good job it does.
(I think it helps that I have some functional programming experience under my belt, and that I tend to use mutability sparingly, and mostly in very simple ways.)
This needs to be Rust's motto or something.
For those stuck with C++, you're not out of luck in terms of safe and easy sharing between threads. The SaferCPlusPlus library provides "access requesters" that provide functionality and (data race) safety similar to Arc<T>.
Unfortunately the documentation[1] is "in transition" at the moment and does not currently refer to the very latest version. But the main difference is that the latest version implements something akin to rust's "sync" trait to help prevent you from attempting to share inappropriate data types.
Again, documentation is a work in progress, but if you're interested you can check out an example that demonstrates an array being safely modified simultaneously from multiple threads[2].
[1] shameless plug: https://github.com/duneroadrunner/SaferCPlusPlus#asynchronou...
[2] https://github.com/duneroadrunner/SaferCPlusPlus/blob/7e3574...
Oh and the tooling! I hope other projects take a serious example how cargo works. It's very hard to use any other build system.
Actual crashes (due to segfaults) can happen a long way from the bug that actually caused the issue, can happen intermittently and generally be a nightmare to debug.
> Given that this Servo code replaces an existing code base, couldn't we get a "guestimate" by looking at how many unsolved bug reports are now closed because their associated previous (presumably C++) code has been replaced? How many open bugs existed in Stylo's precursor that are removed now?
> I don't think there would be many of these. Specific threading bugs once found get fixed, and we don't know how many go undetected.
The fearless concurrency really is ass-saving. For example, my most recent non-bug: I launched two parallel tasks where one would free a shared resource when done. In C that would be intermittent use-after-free. In Rust it was a compile-time error.
Accounts like this one really help people who advocate investing in and building new tools to help against the heavy-handed application of phrases like "a bad workman always blames his tools" [0][1].
[0] https://en.wiktionary.org/wiki/a_bad_workman_always_blames_h...
[1] https://en.oxforddictionaries.com/definition/a_bad_workman_a...
Edit: formatting and typo
It nicely suggests that maybe it's the programmer's responsibility to advocate for something better.
My only problem is that it drains my battery life fast. So whenever I'm not plugged I use Edge. Other than that, FF is amazing.
It is now my default browser and I even wrote a FF add-on a few days ago using their new API!
However Firefox has been, and always will be my daily driver for all Web browsing. It's eco system is richer, noscript and the fact that it's not a Google product is a huge selling point.
* selecting CSS colors in other colorspace (eg. RGBA) as oppose to just hex
* throttling the network in desktop mode. I think the option just shows up in responsive design mode.
Although I dont think Chrome is "vastly" superior at this point as FF has certainly narrowed the gap.
> I've tried the Firefox inspect menu and I'm just immediately turned off and confused..
I remember feeling the same when I switched from Firebug to Chrome. It takes a few days to get used to a new UI.
If you start learning C++ it's relatively smooth sailing at first, especially if you're already familiar with C. Basic OOP, basic RAII, inheritance, virtual functions, basic templates. Easy peasy.
It's once you start getting to the advanced topics that the footguns become apparent. The sometimes intricate resolution rules (and how they compound with template substitution rules), the various subtleties surrounding copy constructors, const, mutable, concurrency and the way they play with each others, the various quirks inherited from C that sometimes don't play very well with modern constructs etc...
Rust is the other way around. There's a very steep curve right at the start where you need to understand how the borrow checker works and how to make it happy. You have to learn the right mindset right away. You need to get over that to reach the "fearless confidence" goodness.
I think that's going to be a big problem for experienced C++ coders to do the jump (especially if you need to convince multiple devs to make the jump at the same time).
It kind of reminds me of the switch from SVN to git. At first I didn't get it, git felt a lot more complicated and I didn't really see the benefit compared to good old SVN. Of course after a few years I'd curse under my breath every time I had to use SVN for some legacy codebase, it feels so clunky and limited now that I'm familiar with a proper git workflow.
Rust is a clear winner over either in my view because of its traits system and immutable-by-default story. It feels like the kind of C++ I write, but with less boilerplate and typing. The safety story is irrelevant in our case (it's nice, but wasn't an explicit factor in the decision: the other features in Rust may have been developed to support safety, but safety need not exist to provide them). The biggest pain point is that it's not an appropriate language for high performance numerical computation, so we'll have some "glue" there. How that will work is yet to be determined, but the data engineering and infrastructure around the pipeline is definitely going to Rust.
EDIT: I meant a code example, not a paragraph. And I would obviously expect to see how the intended goal is achieved without the bug... otherwise it'd be trivial to prevent any bug (just make everything impossible).
fn use_lock(mutex: &Mutex<Vec<i32>>) {
let vec = {
// acquire the lock
let mut guard = lock(mutex);
// attempt to return a borrow of the data
access(&mut guard)
// guard is destroyed here, releasing the lock
};
// attempt to access the data outside of the lock.
vec.push(3);
}
It doesn't compile because the lock is not held long enough. error: `guard` does not live long enough
access(&mut guard)
^~~~~
There are several more examples in that article, but you can read them there rather than here! template<typename TVectorPointer>
void write_foo(TVectorPointer vec_ptr) {
(*vec_ptr)[3] += 1;
}
template<typename TVectorPointer>
auto read_foo(TVectorPointer vec_ptr) {
return (*vec_ptr)[7];
}
template<typename TVectorAccessRequester>
void use_shared_vector(TVectorAccessRequester vec_ar) {
{
// obtain a non-const pointer to the vector from the "access requester"
// blocking if necessary
auto wl_ptr = vec_ar.writelock_ptr();
write_foo(wl_ptr);
// the pointer owns a lock on the vector so as long as the pointer
// exists it is safe and valid to use
}
// obtaining and using a const pointer to the vector
auto res1 = read_foo(vec_ar.readlock_ptr());
// without a "lock pointer" obtained from an access requester, there is no way
// to access, or even refer to the shared vector, so you can't access it in an
// unsafe manner
}
[1] shameless plug: https://github.com/duneroadrunner/SaferCPlusPlus#asynchronou...Rust doesn't allow having two mutable references to the same memory location at the same time, so you would never encounter that.
Wouldn't that make a whole class of efficient algorithms impossible though? Like let's say you have an std::list<T> and you want a sorted "view" of the elements. In C++ you'd create an array of pointers and then sort it, and after that you can just modify whatever each slot points to. In Rust you... can't do that because they'd all need to be read-only? I don't imagine you can turn the read-only to read-write on a whim (otherwise if you do this with two of them how the hell would the compiler figure out if two of them point to the same slot?) so the whole thing is just impossible?
You are forced by the compiler to implement an explicit exclusion mechanism.
The point is that kind of issue are silent bugs most of the time(up to point) in C, C++. It can work in most cases, and one day, something goes awfully wrong because the thread scheduling is slightly different than usual.
For example this code, which tries to send a reference-counted pointer between threads, which can cause the reference counter to become unsynchronized and random use-after-free:
use std::thread;
use std::rc::Rc;
fn main() {
let rcs = Rc::new("Hello, World!".to_string());
let thread_rcs = rcs.clone();
thread::spawn(move || {
println!("{}", thread_rcs);
});
}
Is detected by the compiler and causes this error error[E0277]: the trait bound `std::rc::Rc<std::string::String>: std::marker::Send` is not satisfied in `[closure@src/main.rs:8:19: 10:6 thread_rcs:std::rc::Rc<std::string::String>]`
--> src/main.rs:8:5
|
8 | thread::spawn(move || {
| ^^^^^^^^^^^^^ `std::rc::Rc<std::string::String>` cannot be sent between threads safely
|
= help: within `[closure@src/main.rs:8:19: 10:6 thread_rcs:std::rc::Rc<std::string::String>]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::string::String>`
= note: required because it appears within the type `[closure@src/main.rs:8:19: 10:6 thread_rcs:std::rc::Rc<std::string::String>]`
= note: required by `std::thread::spawn`Rust prevents that by having the concepts of "ownership" and "borrowing" built into the language. The Rust book probably explains this better than I ever could, but the basic idea is that you can only have one actor writing to a variable, OR any number of actors reading a variable. But you cannot have multiple actors having write access the same variable at the same time, or one actor writing to it while anyone has read access to it, unless you use some sort of serialization or copy-on-write mechanism.
Firefox isn't slow, sure enough. But it wasn't slow last week either. I've been using it as my primary browser for a long time, and performance was never an issue.
Also, 'Quantum'? Come on now. If they're aiming Firefox at power-users (which they should be), they should know that kind of buzzword abuse is only going to annoy.
I switched to Firefox 2 days ago, after reading a post about how Firefox has recently got faster. I have been a Chrome user for more than a couple of years now, when I switched from Firefox because it was slow in comparison.
After using Firefox for 1 day, it already seemed slower than what I was used to in Chrome. This was most noticeable on ad-laden sites that displayed video ads or similar. I hadn't installed ad-blockers I had on Chrome yet, so maybe it was just that.
After 1 day of use, I was ready to switch back. But then I installed Quantam yesterday, and boy has it been amazing! It is noticeably faster, without any ad-blockers or extensions.
So atleast for me, it was slower last week, and just got fast.
And yes, it was slow. Performance was not an issue for me either, I've been using it for a long time, but solely because of the features, not the speed, and the difference is noticeable.
Quantum... I like to think of 57 and beyond as a quantum leap from before. They did after all let go of XUL overnight, and people do praise its speed overnight. :-)
Wow! This is great, especially considering how bad and complex (in the bad sense) C++ is. Maybe Rust and Go will finally make the Frankenstein go to sleep
One of the biggest contributors is "custom derive," which cuts out a lot of boilerplate. Another is that Gecko C++ is also pretty old and so doesn't rely on a lot of what is now standard. It's also just a ground-up rewrite, which has the advantage of hindsight.
It's not a trick of moving code around.
Also this:
>Acid3, in particular, contains some controversial tests and no longer reflects the consensus of the Web standards it purports to test, especially when it comes to issues affecting mobile browsers. The tests remain available for historical purposes and for use by browser vendors. It would be inappropriate, however, to use them as part of a certification process, especially for mobile browsers.[0]
Somewhere on the second page I found a brief mention of a data race in rustc itself (that was fixed).
Looks pretty fearless to me!
After some digging I found that the issue is that whenever
src is set and then contentDocument is called right after,
there is a race condition and contentDocument will return
null because the Document hasn't been created in the script
thread yet.
-- https://github.com/servo/servo/pull/14764
I decided not to cherry pick because I figured people could look through themselves and decide. And because culprits are not always found (or at least documented) before a fix is committed. This is how thread races work in any language--they're hard to definitively pin down, not to mention reliably reproduce, but generally easy to fix (or at least make disappear) once something suspicious comes to your attention.Concurrency is easy when you don't share mutable data. But when you do have to share mutable data--core, performance-sensitive shared data structures--Rust hardly makes doing so "fearless". What Rust does is make it difficult to _accidentally_ share mutable data.
Most importantly, though, it preserves _memory safety_ in concurrent situations, so your stuff won't randomly crash, but properly panic.
It's no silver bullet, but it _is_ the "magic sauce" behind Stylo.
To my knowledge you can defeat the compile-time data race detection if you are either doing unsafe or certain scenarios with Cell/RefCell but even in that case you are guaranteed runtime detection rather than compile time detection.
These feature alone is worth its weight in gold.
https://github.com/servo/servo/issues/14014
Of course it's using unsafe code and doing other crazy stuff--a lot of these issues are related to shared, mutable, tree structures. But that's precisely my point. When you're implementing something as sophisticated as Servo and trying to keep things performant and multi-threaded, concurrency is hardly fearless. Servo does this and they have bugs.Indeed, being "fearless" is precisely how you end up with these bugs, in Rust or any other language. If you're fearless you're more apt to move from a big lock to a fine-grained locking mechanism. That's error prone, including in Rust.
It's like that dude in Florida whose Tesla flew under the tractor trailer. He was fearless in the same way inexperienced engineers using Rust will be when they hear "fearless concurrency". They'll push the envelope when they have no need to, because that's what inexperienced engineers do who haven't been burned, especially when they think their tools make them fire-proof.
I just take issue with "fearless concurrency". All concurrency is fearless if you use a share-nothing architecture. But often times for performance you can't do that. And while Rust may be better than most languages about making it more difficult to screw up, even the Servo folks create race conditions.