# Reading club: Linear types ###### tags: `reading-club` Link: https://blog.yoshuawuyts.com/linear-types-one-pager/ ## Different Designs ```rust // current Rust fn drop<T>(value: T); // complete manual system trait Leak {} fn drop<T: Leak>(value: T) {..} fn identity<T: ?Leak>(value: T) -> T {..} // auto-traits auto trait Leak; impl<T> Leak for T {} fn drop<T>(value: T) {..} // `T: Leak` bounds are inferred fn identity<T: ?Leak>(value: T) -> T {..} // maybe-leak effect (superset a la `async`) fn drop<T>(value: T) {..} !leak fn identity<T>(value: T) -> T {..} // linear effect (a la `const`) fn drop<T>(value: T) {..} linear fn identity<T>(value: T) -> T {..} // label the entire function fn identity<linear T>(value: T) -> T {..} // trait transformers syntax ``` _Note: I (Yosh) don't really like `linear` as a keyword, so it's more for illustration purposes. Imo `leak` seems better, but that would imply `!leak fn`, which maybe is a bit weird - but again idk. Exact syntax is sort of the least important thing here imo._ (eholk) Nice seeing options laid out like this. Opt-in or opt-out. Opt-in would probably be less disruptive. (yosh) Would be nice if we could start with an empty set of effects (`total`), and make all other effects opt-in on top of that. But that's not really what we have today, but maybe something to strive for? idk Koka paper: https://arxiv.org/pdf/1406.2061.pdf ## Fork the Compiler? (eholk) This seems like the kind of language change where (temporarily) forking the compiler makes sense. It means we can have a trial period before adding an insta-stable `+ ?Leak` bound to everything in std, but also would give people a chance to try using the language to get a sense of how usable it is. (As far as I [eholk] know, no one has shown that true linear types work ergonomically at scale, so this would give us a chance to demonstrate that.) Yosh: the changes you would need to make to the stdlib is disallow: - statics - everything that's `ManuallyDrop` (`mem::forget`) - everything that's `UnsafeCell`-based (`Rc`, `Arc`, `Mutex`, channels) - `thread::spawn` Eholk: Do we need `UnsafeCell` for `1:1` channels? Yosh: that's a good question! eholk: an implication of channels needing `UnsafeCell` is that linear types cannot be `Send`? Maybe we need a linear channel? Yosh: I think we can make linear types `Send`, but I don't know if we can create linear channels? ## Shared Ownership, Interior Mutability, Guaranteed Destructors - Choose Two (eholk) If I remember right from the leakpocalypse, the problem was that `Rc`/`Arc` combined with interior mutability was how we got in to trouble. It seems like maybe the primary contribution of this type is that it lets us annotate either `Rc` or `UnsafeCell` with `!Leak` to let us choose another side of the triangle, but which one should we pick? (eholk) Is Interior Mutability + Guaranteed Destructors possible? Yes! `Box<RefCell<T>>` can't make cycles by itself. (eholk) Can't make cycles with just `Arc`, except if you use the cyclic constructors. [`new_cyclic`](https://doc.rust-lang.org/std/sync/struct.Arc.html#method.new_cyclic) [Gankra's safe forget code](https://faultlore.com/blah/everyone-poops/): ```rust fn safe_forget<T>(data: T) { use std::rc::Rc; use std::cell::RefCell; struct Leak<T> { cycle: RefCell<Option<Rc<Rc<Leak<T>>>>>, data: T, } let e = Rc::new(Leak { cycle: RefCell::new(None), data: data, }); *e.cycle.borrow_mut() = Some(Rc::new(e.clone())); // Create a cycle } ``` Eholk: you need both `Rc` and `RefCell` to make a cycle. That means interior mutability + shared ownership. Yosh: does that then mean that we can leave `Arc` alone and only disallow the `UnsafeCell`-based `Mutex` etc types? Eholk: I think so! Eholk: Do we want just interior mutability without shared ownership? No! If you have unique ownership you can already mutate, so interior mutability is only interesting with shared ownership. Yosh: There seems to be a connection between tree shaped data and tree shaped computation (structured concurrency) - exactly what that connection is is unclear. ## Connection to async drop Yosh: had a chat with Niko on this a few weeks ago. We sort of settled on that async Drop and linear Drop probably shouldn't be the same feature? Yosh: we also discussed what a full async drop would look like - if it propagates the way current `Drop` propagates, then we'd probably need a way to tag structs as async. So if you nest a struct with an async destructor inside of another struct, the destructors are still run.