This could be used as an ad for Rust borrow checker, verbatim. You can't modify a map or slice you passed as an argument if a reference to it is stored!
This could be used as an ad for Rust borrow checker, verbatim. You can't modify a map or slice you passed as an argument if a reference to it is stored!
For the record, rust does also default to immutability, and you can share immutable references all you'd like.
Ownership and move semantics are checked at compile time, and allow effective "not sharing" without waiting or defensive copies.
In reality it's part of a different language which exposes a lot of complexity to the programmer as opposed to Go, which religiously avoids that.
I think that rust fills the needs of applications with extreme requirements. A browser is in that niche. Not all software is.
Thus most of the time you don't have to inflict yourself the pain to work with it.
Anyway... Languages with GC still do have ownership. It is just defaulting to be the most problematic case of "shared ownership" everywhere. Shared ownership is only worry-free if you don't ever modify data. Which is possible, but actually harder and more constraining to implement in a practical software (especially if you care about any performance). So then you either have to synchronize everything with mutexes, and figure out a way to not deadlock yourself on every step (also hard), or you have to implicitly track ownership and decide who can and when mutate the data, which is exactly what Rust would make you do, but this time you get no compiler help. The only reason people don't notice that is because they do it incorrectly and it works most of the time, so they are unaware.
There is no escape from it - one has to figure out ownership with a GC or without it, because GC has nothing to do with ownership. It only reclaims memory after all references are gone. That's it. That is actually very, very little improvement.
Ironically, that's Rust biggest problem: not using perfect ownership management works for most software most of the time and that's good enough for most people and companies.
You probably can't sell ownership management as a feature. The only thing that Rust can sell and that makes sense is "it won't crash" + "it's fast", but then there's a lot of other languages that have the first property and the second is not very appreciated, as seen in the rise of so-called Electron apps and web apps in general.
I think in time more and more people and teams will discover that doing stuff with Rust is just cheaper and more productive. You learn ownership once, you get great results ever after.
It seems likely that you and I are using different definitions, and yours Rust-specific (and therefore not meaningfully generalizable to other languages, especially those with GC).
I mostly write Java for a living, have done for a decade, and still occasionally trip myself up by mutating an object I'd forgotten I've shared.
Go is a very effective tool in the hands of experienced programmers, without having to pay the cognitive load price of 'Mommy' languages. I am reminded of Bryan Cantrill's rant regarding threads in this context.
Writing solid software is hard. It takes skill, experience, and serious battle scars. Tools and languages can help, but at the end of the day, it comes down to the team that writes the code.
This should not be news to this audience on HN: proper software architecture, design, and development processes trump "language" any day, any time, any planet. Conversely, even the most 'profoundly correct' language in the hands of unprepared development teams can result in software disasters.
The human factor is still paramount in software development. That is my experience after decades in this profession and is not mere conjecture.
More typically you're building a new slice containing the results of a query or other computation, and returning it without keeping a reference. Or instead of exposing an internal map, you have a Get method.
It comes up in any language that allows shared mutable state, so not much in functional languages which discourage mutability or Rust which discourages shared mutability.
On the other hand, most code tries to avoid shared mutable state by convention, there’s a nice bit in the Go lang design article that basically says convention is good enough, significantly simplifies the language, at the cost of the odd bug.
Many Go libraries like to offer passing a byte slice to reuse as a destination. Many Go libraries take byte slices or structs containing them as arguments. A common result is "loop over some input, read it into the reusable slice, pass the slice to the next step". It even sort of works - until the next step does something asynchronous.
It doesn't help that:
- Lots of things return a new slice "sometimes" - _this_ append() result is safe to pass on; this other one is not; this other one "it depends".
- The `x[:]` idiom to turn an array into a slice looks exactly like a Python copy, but actually shares the same backing store.
In the end we basically banned reusing slices as a result. (The rule I tell new programmers is "if you pass the slice elsewhere in a loop, the slice needs to be allocated within the loop.") Which is a shame, because the performance gain from safe reuse is often significant. But until the type system blocks us from changes, usually in the oblivious callee, which turn a safe use into an unsafe one, it's simply too much effort to remember and review each case.
var buf bytes.Buffer
buf.WriteString("foo")
b := buf.Bytes()
fmt.Printf("b == %v\n", string(b)) // b == foo
buf.Reset()
buf.WriteString("bar")
fmt.Printf("b == %v\n", string(b)) // b == bar
Yes the documentation for .Bytes() says that "The slice is valid for use only until the next buffer modification" but people don't always read the docs for every method they use, especially if it's a method they've used a bunch before. Having a reusable buffer that you return the .Bytes() value from is very tempting. Bug-free code would either copy the result or not reuse the buffer.In Rust, the borrow checker is too a separate static analysis stage and not directly tied to code gen. There are Rust compilers without the borrow checker.
The go authors wouldn't accept it, the ecosystem wouldn't work with it, and it would be fighting an uphill battle rather than just using rust or building a new language.
> There are Rust compilers without the borrow checker
Then those compilers do not compile rust. They compile a bastardized version of rust whereby programs that are not valid rust programs can compile.
Also, can you name these non-borrowck-ing rust compilers? You say there's multiple, so you should be able to at least name one or two.
You can't have the best of both worlds. You win some and lose some. Rust avoids this but loses flexibility.