If you disallow mutation of values in a collected context, then it Just Works. The only way to complete a cycle is by mutating a managed value or &quot;tying the knot&quot; via laziness (which is of course just mutation in a hat).<p>Rust almost has this. Only shared (immutable) references can be obtained to values owned by a reference-counted pointer (which is nothing fancy -- just not implementing mutably dereferencing RCs). Since mutability is inherited, this freezes all reference counted data and everything it can reach.<p>However Rust also provides types which specifically <i>can</i> be mutated in a shared context (a Mutex being the most obvious case), so you can indeed make reference-counted cycles and leak.<p>Still, by default you can&#x27;t leak. You have to opt into these &quot;interior mutability&quot; types.<p>Basically, &quot;shared XOR mutable&quot; does not necessitate forbidding mutability universally. It just requires a way to freeze data when it <i>is</i> shared. You can even unfreeze it whenever you can prove it isn&#x27;t actually shared (e.g. reference count = 1).