Rust has the notion of borrowing or moving parts of a struct, but can’t express in the type system a struct type that has some members inaccessible due to being borrowed or moved out.
This means, for example, that if you keep a borrow &self.foo, you can no longer call any method on &mut self, even if that method only really needs to modify self.bar and doesn’t touch self.foo. To work around this, you are forced to replace the &mut self with individual &muts to fields, which can be unergonomic.
So a possible idea could be to introduce “partial struct types”, which would be denoted as StructName {field1, field2 {subfield1, subfield2, …}, …, fieldn, &borrowed1, …, &borrowedn}. This type would be exactly like StructName, except that only the named fields (and recursively subfields if specified) can be accessed, and the ones with the “&” only support taking an & but not an &mut. Perhaps a !field1, !field2 syntax could be introduced to denote “all fields except field1 ad field2” (also supporting the &).
Types of struct variables would automatically “morph” into partial struct types as borrows happen. Essentially, instead of keeping that state in the borrow checker internally, it would now be made explicit in the type of the variable itself.
For private methods and public methods called from the same crate, the types of parameters would be automatically replaced with the most restrictive partial struct type, and you would thus be able to call methods that would trigger the borrow checker otherwise.
Downsides and possible issues:
- Adds a complex subtyping hierarchy, not sure of the impact on type inference, etc.
- Requires (per-crate) global type inference, since typing out the partial types would be too annoying
- Might be challenging to implement while keeping compilation times low
- Inference on private calls can result in “action at a distance” where modifying some code results in borrow checker errors far away
- Authors of libraries with structs that have public fields (or that take parameters that are &/&mut of a struct with public fields) would have to explicitly decide whether to use partial types for each of the methods.
Extensions:
- Could do it with enums too, after having implemented first-class types for variants