3 ms·
Er... > Code written in Node.js and Go could be made memory-safe with enough work, but they're both GC based, which inevitably leads to engineers easily missin
by rob74 5y ago
Er...
> Code written in Node.js and Go could be made memory-safe with enough work, but they're both GC based, which inevitably leads to engineers easily missing memory safety issues
And here I was (maybe naively) thinking that a garbage collector ensures memory safety? Unless they mean memory leaks (of the kind where memory is allocated and can't be freed by the GC, but is also not used?!). But that should be relatively easy to catch with a profiler? Maybe some more explanation would have been in order here...
More generally, glad to see Go got 80% compared to Rust's 90% - just hoping they weighed their priorities correctly, otherwise who knows, with such a small difference Go might have come out on top...
- cillian64 5y agoGC protects you against use-after-free memory safety issues but doesn’t help with concurrency issues, i.e. one thread reading from a shared structure at the same time as another writing to it.
- nkozyra 5y agoI wonder how much work they estimated it would take to make Go as "memory safe" as Rust. I feel like the body of the post could have been "we just want to use Rust, ok??!"
- EdwardDiego 5y agoYeah, I feel like you could just rephrase Jim Jefferie's classic comment on gun control[0] and replace this entire blog post with "fuck off, I like Rust". [0]https://youtu.be/0rR9IaXH1M0?t=107 https://youtu.be/0rR9IaXH1M0?t=107
- MaulingMonkey 5y agoI agree that leaking memory usually gets its own category, rather than being shoveled under the "memory safety". That said, I'll play a bit of devil's advocate: You can still have use-after-free bugs in a GCed language. A trivial example is calling methods on a closed/disposed/??? socket, file, or other handle. They don't result in the arbitrary memory corruption I'd typically consider a "real" memory safety issue, but they do result in bugs. In some cases, debug allocators and tools like address sanitizer can even surface such mistakes more quickly than similar code in a GCed languages. (Of course, in other cases, mistakes with non-GC memory cause bugs that are an absolute nightmare to repro and debug.) More troubling is native interop, which I'd assume something like Synergy will definitely need to do plenty of to capture/forward keyboard/mouse input, bypassing the traditional window focus reliant input stack that the language may pre-provide safe bindings for. Failure to pin, or unpinning too early during an asyncronous operation, can cause your GC-allocated memory to shift around during GC compaction when native code was expecting it to remain in one place. The GC can also potentially outright reap GC-allocated objects that native code was using. I remember reading of nasty heisenbugs from Ruby C extensions forgetting to use RB_GC_GUARD to ensure VALUEs remain on the stack, where the GC can find them, when scanning for GC roots that keep objects alive - as an example. Well, you can write similar bugs in non-GCed languages, but GCed languages aren't immune from these concerns. I've even bluescreened fairly modern Windows, with C# code, by sufficiently misusing SlimDX! EDIT: links to the aforementioned reading: http://timetobleed.com/the-broken-promises-of-mrireeyarv/ http://timetobleed.com/the-broken-promises-of-mrireeyarv/ https://news.ycombinator.com/item?id=2730286 https://news.ycombinator.com/item?id=2730286
- Rochus 5y ago> You can still have use-after-free bugs in a GCed language. A trivial example is calling methods on a closed/disposed/??? socket, file, or other handle. What prevents you from doing such things in Rust?
- MaulingMonkey 5y agoSuch operations often consume `self` in Rust, resulting in compile time errors: use std::io::{self, *}; fn main() -> io::Result<()> { let mut f = std::fs::File::create("asdf.txt")?; writeln!(f, "asdf")?; drop(f); // closes asdf.txt writeln!(f, "asdf")?; // error[E0382]: borrow of moved value: `f` Ok(()) } Full diagnostics: error[E0382]: borrow of moved value: `f` --> src/main.rs:7:5 | 4 | let mut f = std::fs::File::create("asdf")?; | ----- move occurs because `f` has type `File`, which does not implement the `Copy` trait 5 | writeln!(f, "asdf")?; 6 | drop(f); | - value moved here 7 | writeln!(f, "asdf")?; | ^^^^^^^^^^^^^^^^^^^ value borrowed here after move | = note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info) For more information about this error, try `rustc --explain E0382`. Of course, a socket timing out or a drive getting unmounted could still yank a handle out from under these APIs, and e.g. C++ still allows for using such zombie objects. There are also ways to help catch such bugs in GCed languages as well (be it ObjectDisposedException making such bugs shallower in C#, or using lambdas/blocks for scopes to turn continued object use into a syntax error:) # Ruby File.open("asdf.txt", "w") do |f| f.write("asdf") end f.write("asdf") # f wasn't in scope Ruby's dynamic nature means this will still fail at runtime, but similar patterns in C# can force compile time syntax errors for use after close. EDIT: You even see this pattern in Rust, occasionally, to limit reference lifetimes to avoid soundness issues (e.g. all the annoying `.with(...)` blocks you see when using thread_local! : https://doc.rust-lang.org/std/thread/struct.LocalKey.html https://doc.rust-lang.org/std/thread/struct.LocalKey.html ).
- Rochus 5y agoThat's interesting, thanks; with a GC language one would raise a runtime exception instead.