4 ms·
> One short answer is anything that needs dynamic linking. Rust has no stable ABI. In theory this is problematic, but I'm not sure how often it comes up in pra
by dbaupp 9y ago
> One short answer is anything that needs dynamic linking. Rust has no stable ABI.
In theory this is problematic, but I'm not sure how often it comes up in practice: a lot of systems-y use cases for dynamic linking are writing low-level libraries that many other languages can use, which currently means exposing a C interface. This is supported and stable in Rust.
Of course, this doesn't cover every thing, e.g. pure Rust plugins for pure Rust programs. Although one still can dynamically link, as long as all pieces are compiled with the same version (annoying, but, depending on circumstances, not a complete roadblock due to tooling like rustup).
> Imagine if JavaScript's `getElementById()` threw exceptions if the referenced element was already on the stack
I'm sure you realise it, but Rust does offer a wide variety of functionality for working with shared data. It is definitely syntactically more verbose than in languages that don't try to tame mutation, but it's essentially equivalent functionality (at least, to languages that use reference counting).
- millstone 9y ago> a lot of systems-y use cases for dynamic linking are writing low-level libraries that many other languages can use, which currently means exposing a C interface. This is supported and stable in Rust UIKit is an example of a high-level dynamically linked library. Rust isn't ready for that category of use case - but to be fair neither is C++ :) > Rust does offer a wide variety of functionality for working with shared data. It is definitely syntactically more verbose than in languages that don't try to tame mutation, but it's essentially equivalent functionality My point regarding shared data is not about Rust's verbose syntax, but about its unwanted runtime checks which no other languages perform. Here's what I have in mind (doing my best pseudo-Rust): struct CounterWidget { count: u32, limit: u32, didClick: fn(), }; impl CounterWidget { fn clicked(&mut self) { if self.count < self.limit { self.count+=1; didClick(); } } fn setLimit(&mut self, limit:u32) { self.limit = limit; } } The Counter widget has a count and a limit, and a callback for when it's clicked. It also has a landmine: if the click callback attempts to call setLimit on the same Counter, it will crash at runtime. This crash is hard to test for and doesn't add any value: why shouldn't the callback be able to adjust its limit?
- tatterdemalion 9y agoIt took me a bit to figure out what you could be talking about. Rust does not insert runtime checks like this of anything by default except for boundschecking on indexing operation. You must mean that you stored your `CounterWidget` in a RefCell, which does have the semantics that if you mutate it while holding a mutable reference to it, it will crash. But your code sample didn't include a RefCell at all, so this is very unclear. Yes, its true - Rust provides a hard guarantee against simultaneous mutable access to the same data. This is invaluable even in a non-concurrent context, both because of memory issues like iterator invalidation & higher level logic errors that result from silent mutation at a distance. I can assume looking at your code that didClick() doesn't mutate this CounterWidget - or even access it. This means I know that if I rearrange the counter increment with the didClick callback, I know nothing is semantically different. I can introduce additional code to the function, knowing that the state is the same after didClick as before it. All of this information is very useful to me when I'm trying to maintain this codebase (not to mention useful to the optimizer). If you want to be able to provide mutable access to the callback, just make it an `fn(&mut CounterWidget)`.
- dbaupp 9y ago> UIKit is an example of a high-level dynamically linked library. Rust isn't ready for that category of use case - but to be fair neither is C++ :) That's fair. > My point regarding shared data is not about Rust's verbose syntax, but about its unwanted runtime checks which no other languages perform. FWIW, I think Swift is the only language with implicit checks of this form (for global variables and class properties). Rust only performs such checks if the programmer opts into it by using types like RefCell or Mutex. > It also has a landmine: if the click callback attempts to call setLimit on the same Counter, it will crash at runtime. Specifically, the callback will be statically unable to call setLimit unless the programmer decides to use one of those tools, in which case the location of the crash is clear. > This crash is hard to test for and doesn't add any value: why shouldn't the callback be able to adjust its limit? That specific case seems like it's okay, but there's a pile of problems in code that is only slightly different. For instance, - if `clicked` held a reference to an element of a vector stored inside CounterWidget across the callback call, allowing mutation would allow that reference to be invalidated (this is less contrived than it sounds: e.g. iterating through a vector calling the callback for each element has this problem). This is undefined behaviour. - if the callback enables results in the data being manipulated on multiple threads, you get a data race. (Also undefined behaviour.) - stepping away from undefined behaviour, there may be things inside the `if` statement after the callback that are relying on the invariant the condition established (e.g. `let remaining = self.limit - self.count` and assuming it will be > 0), the callback can invalidate this in surprising ways, both for the programmer, who didn't expect the circular chain, and for the compiler's, which is thus forced to be defensive/pessimistic, meaning slower code (this is one major reason why Swift adopted exclusivity).