5 ms·
As I read through the database example, I saw that the compiler just caught a multi-threading bug for the author, and instead of being thankful, he’s complainin
by xiphias2 6y ago
As I read through the database example, I saw that the compiler just caught a multi-threading bug for the author, and instead of being thankful, he’s complaining that Rust is bad.
I think he should use a higher level framework, or wait a few years for them to mature, and use a garbage collected language until then.
- joubert 6y ago> caught a multi-threading bug The compiler complained: “error[E0308]: mismatched types”
- xiphias2 6y agoThat’s why it’s amazing, type safe efficient multi-threading without dynamic memory allocation with a nice syntax...it’s the holy grail of server programming.
- literallyWTF 6y agoYeah I don’t get it either. We’re supposed to be upset that it did it’s job? Lol?
- andrewflnr 6y agoIt would be nice if it was explicitly a borrowing-related message. That's the kind of thing I can see happening in a future rustc actually.
- xiphias2 6y agoYeah, the good thing is that the devs are always happy to take feedback on improving the error messages (like explaining differences between Fn/FnMut/fn here)
- thu2111 6y agoWhy would I want to write a server in a language that requires such awkward approaches to basics like coroutines and dynamic dispatch when I could use kotlin on the JVM and get pauseless GC, ultra fast edit/compile/run cycles, efficient and powerful coroutines, and eventually Loom which will eliminate the whole problem of coloured functions completely and let me just forget about coroutines? Multi threading bugs are not so common in real Java/Kotlin programs to justify this epic set of costs, in my view.
- ha_jo_331 6y agoOr even better: Go
- roca 6y agoGC always forces you into a tradeoff between pause time, throughput, and memory overhead.
- karlmdavis 6y agoHave you ever tried writing the same application on both a JVM language and in Rust, and then measuring the latency & throughput differentials? Blew my mind the first time. Of course, if speed isn’t a concern for you, then please carry on.
- xiphias2 6y agoGood luck getting Google's ad backend with 20ms latency budget and billions of dollars of revenue approved with the JVM. Therr are many tasks where what you suggests is just too slow and unpredictable. Don't get me wrong, I think JVM is great, it's just not systems level programming.
- thu2111 6y agoModern JVM GCs have pause times below 1msec. That's a new capability though so most people aren't yet aware of it. And Google's ads backend are hardly the definition of server. Their ads front-end for example always used to be in Java. Not sure what it is these days
- redis_mlc 6y agoEverything is "mismatched types" in Rust, literally it doesn't do any automatic type conversion (casting), so it's not the right language for most people.
- agumonkey 6y agoI believe haskellers would love (and maybe did) to encode commutativity and thread-safety in the type system :)
- fulafel 6y agoThere wasn't any multi-threading in that example I think.
- Cyph0n 6y agoYes, there was: the function was calling the passed in closure from a different thread. It is just one thread that’s actually appending to the Vec, but it’s still a multi-threaded example.
- fulafel 6y agoAre we talking about the same example? struct Database { data: Vec<i32> } impl Database { fn store(&mut self, data: i32) { self.data.push(data); } } fn main() { let mut db = Database { data: vec![] }; do_work_and_then(|meaning_of_life| { println!("oh man, I found it: {}", meaning_of_life); db.store(meaning_of_life); }); // I'd read from `db` here if I really were making a web server. // But that's beside the point, so I'm not going to. // (also `db` would have to be wrapped in an `Arc<Mutex<T>>`) thread::sleep_ms(2000); } No threads are spawned.
- valand 6y ago` fn do_work_and_then(func: fn(i32)) { thread::spawn(move || { // Figuring out the meaning of life... thread::sleep_ms(1000); // gee, this takes time to do... // ah, that's it! let result: i32 = 42; // let's call the `func` and tell it the good news... func(result) }); } ` you missed a snippet, a thread is spawned. edit: formatting
- imtringued 6y agoIf for some magic reason thread::sleep_ms(1000) takes longer than 2000ms the main function would reach its end and deallocate the closure that is about to get called. Basically use after free.
- EugeneOZ 6y agoYou are to find fancy runtime-only errors in async functions. Their existence outweighs any other compiler’s benefits.
- Deukhoofd 6y agoHe's just pointing out that it isn't very ergonomic, not that Rust is bad (in fact he states the opposite multiple times). Pointing out weaknesses and things that might be done better is what helps something mature, not just praising it.
- filleduchaos 6y ago"Ergonomic" is such a nebulous word as to be nearly useless honestly. I don't see how it is [unduly] inefficient or uncomfortable for a language at Rust's level to ensure that the programmer actually thinks through the execution of the code they are writing. If one doesn't want to think about such things - and there's absolutely nothing wrong with that! - there are plenty of higher level languages that can do that legwork for them with more than good enough performance. To me it feels a bit like pointing out that the process of baking a cake from scratch isn't very ergonomic, what with all the careful choosing and measuring of ingredients and combining them in the right order using different techniques (whisking, folding, creaming, etc). That is simply what goes into creating baked goods. If that process doesn't work for you, you can buy cake mix instead and save yourself quite a bit of time and energy - but that doesn't necessarily mean there's anything to be done better about the process of making a cake from scratch.
- skohan 6y agoI actually find ergonomic to be a very clear and intuitive term as it refers to programming language design decisions. And to use your cake analogy, the fact that a process is complex or laborious is orthogonal to the degree to which it is ergonomic. You could imagine baking a cake in a well organized kitchen with well designed, comfortable tools, or you could imagine having to stand on your toes and reach to the back of a slightly-too-high cupboard every time you need to fetch an ingredient, and having to use a mixer with way too many settings you can never remember, and none that does exactly what you want. I think this is a better analogy for ergonomics in programming languages.
- filleduchaos 6y ago> I think this is a better analogy for ergonomics in programming languages. While it may be a better analogy, it doesn't really reflect the way the term is used. In my (admittedly anecdotal) experience, most people's complaints about ergonomics are more of expecting difficult things to be easier than they inherently are than of the actual quality of the tools they are presented with. People think they are complaining about a mixer with too many settings, when in reality what they are complaining about is the fact that there are several variables that go into mixing batter and dough. They buy a mixer that's targeted at a baker or chef who wants complete control over how their batter comes out or who even needs a mixer versatile enough to also mill grain or roll pasta, then are predictably lost because it's not as immediately intuitive to use as a simple hand mixer. I don't think that makes the mixer not ergonomic, I think it just makes it not the right tool for that particular person.
- gohbgl 6y agoWhat is the bug? I don't see it.
- Cyph0n 6y agoThe bug is that the closure is mutating the same data structure (a Vec in this case) in a different thread - i.e., a data race. In this specific case, only the spawned thread is mutating the Vec, but the Rust compiler is usually conservative, so it marked this as a bug. The actual bug is one or both of the following: 1. One of the threads could cause the underlying Vec to reallocate/resize while other threads are accessing it. 2. One of the threads could drop (free) the Vec while other threads are using it. In Rust, only one thread can “own” a data structure. This is enforced through the Send trait (edit: this is probably wrong, will defer to a Rust expert here). In addition, you cannot share a mutable reference (pointer) to the same data across threads without synchronization. This is enforced through the Sync trait. There are two common solutions here: 1. Clone the Vec and pass that to the thread. In other words, each thread gets its own copy of the data. 2. Wrap the Vec in a Mutex and a Arc - your type becomes a Arc<Mutex<Vec<String>>>. You can then clone() the data and pass it to the new thread. Under the hood, this maps to an atomic increment instead of a deep clone of the underlying data like in (1). The Mutex implements the Sync trait, which allows multiple threads to mutate the data. The Arc (atomic ref count) allows the compiler to guarantee that the Vec is dropped (freed) exactly once.