Bronze GC and aliasing problems

The bronze code looks mostly like a copy-paste of rust-gc, with unsound methods added to create aliased mutable references..

Also, I don't know if maybe manishearth has licensed the code separately to them, but bronze released obviously copied code under a different license (BSD instead of MPL-2).

Granted, it's just a hacked together stub API (with no actual collector) in order to conduct the research study, but I find the entire premise of the study quite odd. The study seems to be trying to answer the question "is Rust easier to learn if mutable references are not unique?" Is that even worth answering? Pretty much everything about Rust would be unsound with shared mutable references.

15 Likes

Some of the code is indeed from rust-gc, with appropriate attribution. My understanding is that the licenses are compatible, but if I'm wrong about this, I could look into changing the license.

The Bronze project is exploring the usability costs of the restrictions that Rust imposes. Indeed, if one uses as_mut() and as_ref() inappropriately, one can do unsafe things. As the project proceeds, I think it will be worth considering (1) dynamic safety enforcement, via a mechanism similar to the Ref/RefMut one used by RefCell; (2) a mechanism to avoid exposing references into the interior of objects owned by the garbage collector.

IANAL;

The licenses are compatible in the sense that you may include code licensed under both in a single distribution. However the MPL is still a copy-left license, and code present in files licensed under the MPL is not allowed to be re-licensed under a more permissive license like BSD.

The files licensed under MPL and any modifications to those specific files must continue to be licensed under the MPL. You may even be required to retain the file header specifically.

Them being "compatible" just means you are allowed to include additional files licensed under BSD, it doesn't change the terms of the code from the original files.

4 Likes

Interesting, so like an automatic RefCell without the Ref(Mut)? It sounds like it'd end up basically like the stacked borrows implementation in miri..

Still, the study allowed students to create multiple live mutable references simultaneously though, which is UB in Rust and contradictory to most of the Rust code currently written in the ecosystem (including libstd), making Bronze essentially a new language. The simple Vec::push example above shows why.

1 Like

Indeed, this is language design research. But I view this as an early-stage step toward compatibility with Rust, not with the goal of developing a fundamentally different end language. But as you say, there is still substantial work to be done before something like this could be considered for adoption in Rust.

Some people have said things like: "there's no need for GC in Rust, because you can already do everything you need to do." What we see in this work is that there is a significant usability cost to that position. Can we build a GC that meets all of Rust's design requirements? That's still a work in progress.

2 Likes

Note that certain files are indeed marked with an MPL license because they are derived from corresponding projects. If I've missed something, I would be happy to fix it.

It's mostly this file which seems to be missing MPL, and I believe you're supposed to include the actual text of their copyright header somewhere.

But obviously it's your judgement call, this is just my guess!

I don't know, this Bronze idea seems a bit wrong-headed to me.

Rust offers you speed AND safety. The high performance comes largely from the efficient use of memory, avoiding garbage collection. I guess that's a good question actually, where does the performance come from?

But regardless, yes, it takes a little time to learn how Rust works, and how to use it, but I don't think Bronze is the right way forward! It's throwing all the unique advantages of Rust away. If you want a safe**, easy language, use say C#. But don't expect it to run so fast.

** Although C# doesn't give you safe concurrent programming the way Rust does.

7 Likes

There are already perfectly valid solutions to the "usability costs" of Rust: use Go or any of the adjacent languages with GC, rather than have a go at Rust :innocent:. Rust is defined by its safety and deterministic memory management.

5 Likes

I think the idea is promising and would be interested in seeing how it turns out!

However, because the ergonomics you are looking into is intrinsically linked to the way mutability and sharing interact, they shouldn't be pushed off as something that should be researched later on.

Otherwise, the research will boil down to just another Rc<T> implementation.

3 Likes

Not my place to moderate but I would like to note there seem to be (at least) two broad question directions discussed here:

  • Is the Bronze GC direction safe/viable at all and why? How is the experiment on usability (and teaching) relevant with multiple mutable refs? etc.
  • Does Rust need a GC / shoul it have a GC? Is Rust better off without GC? etc.

I would think that the firs direction may be more interesting to pursue here - is there perhaps a cleaner argument for what is definitely not possible? The second direction, while also a good question in its own right, has been asked discussed many times elsewhere and seems possibly loaded.

(My own take: Most of the Rust ecosystem avoiding GC with clever and efficient memory management is great - as well as the "virtue" of simplicity and efficiency - but there may be situations where a GC would fit the bill (e.g. complex applications now riddled with Rc<RefCell<_>>, also discussed here), so I am excited about people researching GCs in Rust.)

4 Likes

I can wrap my head around Gc<RefCell<_>>. An alternative to Rc and Arc, I get that. Trying to eliminate the RefCell, though, and do it safely? I get lost there.

3 Likes

Ah, I see that I did not write that very clearly.

I would also think that eliminating the RefCell (resp gc::GcCell) from Gc<RefCell<_>> is not possible in general (or at least not so easily/directly) and I was merely arguing for the value of GC research and development in Rust in general. (In addition to pointing out "critique of Bronze" vs "critique of GCs in Rust in general")

2 Likes

That is a reasonable justification for me, but is this discussed in the paper? The paper makes it sound like Bronze could simply make Rust easier to use, but the question that is really studied here is whether Bronze makes it easier to write Rust code at the cost of also making it easier to cause bugs and security vulnerabilities. Bronze gives up on one of the fundamental promises of Rust -- that without unsafe, the program is memory and thread safe. This is the kind of issue which is often handled via a security advisory in the Rust SecDB (there is a special category informational = "unsound" there). The crate README should at least point this out, if it is a known problem.

I searched for a discussion of this trade-off in this version of the paper and could not find one; my apologies if I just overlooked it.

6 Likes

We'll talk about this a bit in the camera-ready version of the paper. Note, however, that we believe this could be made safe via a dynamic mechanism, though for now that's future work. Do you agree that that sounds feasible?

That is good to hear, thanks. :slight_smile:

Any GcRef<T> that only supports shared references can be used to define a MutGcRef<T> := GcRef<RefCell<T>>, which provides some amount of mutation, albeit at the cost of thread safety as well as time and space overhead. (RwLock fixes thread safety but increases the overhead.) So something is possible, but it moves further away from the language Rust is meant to be by adding more overhead. It also introduces possible runtime failures on each object access if the dynamic check fails, leading to a whole new class of bugs -- bugs that can be hard to test for since triggering reentrancy might require very peculiar inputs. Also this API would be different from your as_mut since it would return some kind of guard that updates the dynamic state when it is droped. This can be a serious ergonomic downside. (Or maybe you are thinking of some other way to dynamically check this, that does not require a guard? That would be quite cool, but I am not sure how it is supposed to work. And the other issues would remain.)

So basically, this language would not quite work like the usual GC languages, and it would have new failure modes not present in other GC languages. RefCell is considered as 'avoid whenever possible' for a reason -- reliability is a core value of Rust, and having random panics interrupt the service goes against that.

All that makes me wonder what the lesson is that one would learn from a study which shows that this language is easier to learn than Rust. It is not very surprising that giving up on the zero-cost principle can help improve ergonomics. But how can that lesson help improve Rust which heavily emphasizes zero-overhead? And if we are okay with some baseline amount of overhead, we probably don't want "Rust but with a GC"; there are other design decisions of Rust that probably also should be reconsidered in that case. See for example this blog post for some musings on a simpler Rust-style language.

7 Likes

Most code is not particularly performance-sensitive; previous studies showed that GC, when applied judiciously, has minimal performance impact (see the Cyclone paper). In general, I think ruling out GC for all programs is a premature optimization IF it is possible to gradually (i.e. partially) strategically reduce the usage of GC when the necessity arises. This is what Bronze is intended to enable.

So yes, making this safe would involve additional overhead. The community has accepted that runtime overhead is worthwhile in certain cases: dynamic dispatch is a good example. And yes, there are cases where even that overhead is problematic. But then, of course one wouldn't use GC in code that is known to be performance-critical; it's better to pay the usability cost. Highly-optimized code is typically less readable, harder to write, and bug-prone anyway; we do it when it's worth it.

My vision is that typically, one would not hold dynamic borrows very long. If programming in an OOP style, one would typically pass GcRefs around, and then inside the body of mutating methods, one would borrow, do the mutation, and discard the borrow. So, if programming in that style, runtime errors due to multiple borrows should be rare. My intuition could be wrong here, I admit. Perhaps it could be possible to use a linter-style approach to identify most of the cases in which this would be likely, but I realize that we're in dynamic-land and full guarantees are not going to be possible in general.

I don't view the Bronze project as proposing a fundamental change to Rust. I view it as proposing an additional usage technique to enable gradual adoption of the faster, safer Rust memory management techniques, and as highlighting for the community the real usability costs of the current approach in Rust.

I agree having a good GC story for Rust would be great. :slight_smile: This has been a topic in the community pretty much since the 1.0 release (and probably before).

The paper seemed to be more about studying the impact on learnability than technical work on the GC itself, so I guess I misunderstood what your long-term goals are here. I am looking forward to seeing what else comes out of this. :smiley:

Come on, give authors a break. It's a usability study, not a Rust replacement. It doesn't have to be a complete bullet-proof high-performance production-ready sound solution, only something that doesn't crash too badly for five minutes when participants play with it.

Also Rust has a few features that are nice even if you don't use Rust for its performance or low-level power, so a slower higher-level smaller Rust could still have its uses.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.