Whoa, this is really neat!! From an API perspective Ticket & pawn look superficially similar to RefMut & RefCell::borrow_mut.
Have you considered an ACP? I'm not sure what color I'd paint that bikeshed, maybe Cell::borrow[_mut] and CellRef[Mut]? One downside though is that it is surprising for a "borrow" method to require a T: Default bound.
EDIT: Maybe Cell::take_ref and TakeRef are better names? There is one potential footgun with the API which is that you can call it twice, but maybe that could be partially mitigated by accepting a &mut &Cell<T>?
But allowing pawning twice and returning a meaningless ticket is not useful. That should fail in some way.
Pawning is sort of a dual of RefCell borrowing. With borrowing, the second mutable borrow fails at run time. With pawning, the second pawning fails at run time. The checking hasn't moved to compile time.
The Cell approach is supposed to be mostly for simple, copyable types that don't contain references. RefCell, and compile-time checked versions thereof such as SafeCell, are more for structs that contain references, from which more complex data structures are built.
In my mind the more important distinction is that, when faced with potential data corruption, RefCell opts to panic, while Cell opts for data loss.
You are right that these are both runtime failures. With the current status quo, the only way to move these checks to compile time is to use explicit lifetimes and an "ownership-first" architecture, e.g. with dependency injection, separation of concerns, etc. I typically find that when a C program is difficult to translate to Rust, it is because the ownership is "inverted" relative to what Rust prefers. A hypothetical SafeCell would be a nice addition, though I'm not sure to what extent dynamic-dispatch-heavy and function-pointer-heavy programs can benefit from it.
In my own code I have used Cell<String> and Cell<Option<impl Iterator<_>>>, so anecdotally I have found it useful for more than just simple copyable types.
The thing is does that already exists -- it's called RefCell.
I haven't. It's a cute trick, but I don't use it myself in anything "real", so I can't make a good case for it -- especially since it works fine outside of the standard library since it doesn't affect public interfaces of stuff.
If you try it out and would like it to become a proper thing, rather than just my sketch, then feel free to file one yourself or we could turn that crate into something with actual docs & such.
I think that's fundamental to it, though. Anywhere you can encode in the borrow checker that you can only call it once, you could just use a &mut T instead of a &Cell<T>. (After all, you can get a &mut T from a &mut Cell<T>.)
You might be able to use &mut &Cell<T> to block "obvious" repeated pawns of the same cell, but it won't protect you if you have two different &Cell<T> to the same location. So it's not worth the required trickery to also work on Cell<T> places' autoref.
Hmm, yeah, the normal case is that you have a &Foo where there's a Cell field, and then foo.thecell.pawn(), even if . projected the reference, it'd still be a different reborrow every time.
And making people let local = &foo.thecell; first to be able to local.pawn() doesn't seem worth it.
Line 73 seems incorrect, since if we drop a first, it is b's value that returned to the cell. The correctness highly relies on the drop order. Such implict thing might not be welcomed.