I've been working on a compiler of my own of some sorts, and lately I've run into a number of roadblocks that have made it difficult to move forward. One thing I've found particularly useful lately is looking at the design of rustc; I've been reading a lot of the online documentation and the rustc guide and borrowing some of the ideas therein.
In particular this past week I've been running into some frustrations with how to carry around all of the important things that much of my compiler needs access to (the outputs of name resolution, type information, options/config, a diagnostic printer...). Looking at rustc_middle::ty::TyCtxt
today doesn't seem all that useful to me because this area of the compiler seems to have been heavily impacted by incremental compilation and parallelization (non-goals of my compiler).
So let's take a look back to simpler times, in 2015. In this version, I encounter something I find a bit surprising:
Nearly every field on this type back then was wrapped in a RefCell!
Now, I've made the occasional sparing use of RefCell here and there in some projects, but it's always the exception and not the norm. Those weird cases where I need shared borrows for some reason but don't care about multithreading, and where I can easily guarantee that all of the borrows are "short enough" that they'll never overlap. I'm not sure if I have ever written a pub RefCell
field.
So this really leaves me wondering:
- What advantages might have been offered by having all of these RefCells as opposed to bare fields?
-
pub RefCell
fields feel dangerously open to me, as conflicts could arise between distant pieces of code. Was there/is there a set of conventions to help avoid these scenarios? (e.g. "a method on a struct should not borrow any of the struct's ownpub RefCell
s, and a function that borrows a RefCell should avoid passing its owner around into other functions")