3 ms·
There is, I think, an ownership model that Rust's borrow checker very poorly supports, and for lack of a better name, I've called it hot potato ownership. The b
by jcranmer 1y ago
There is, I think, an ownership model that Rust's borrow checker very poorly supports, and for lack of a better name, I've called it hot potato ownership. The basic idea is that you have a buffer which you can give out as ownership in the expectation that the person you gave it to will (eventually) give it back to you. It's a sort of non-lexical borrowing problem, and I very quickly discovered when trying to implement it myself in purely safe Rust that the "giving the buffer back" is just really gnarly to write.
- stouset 1y agoMaybe I’m misunderstanding, but why is that not possible with a Fn(_: T) -> T
- iknowstuff 1y agoIt totally is https://docs.rs/tokio-uring/latest/tokio_uring/fs/struct.File.html#method.read_at https://docs.rs/tokio-uring/latest/tokio_uring/fs/struct.Fil...
- dwattttt 1y agoAs sibling notes, it is. It's very rarely seen though. One place you might see something like it is if an API takes ownership, but returns it on error; you see the error side carry the resource you gave it, so you could try again.
- IshKebab 1y agoHow is that different to Fn(_: &mut T) ?
- Soft 1y agoIn the former the caller does not retain access to T until Fn returns.
- andyferris 1y agoI think I'm lost. If I give a mutable reference to a function... I can't access it (even read it) until it returns, no? What is different?
- zbentley 1y agoLet's say a function "foo" calls "fn bar(_: &mut T) -> ()". When passing a mutable reference, the lifetime of the object is largely decided by "foo" (with some caveats). Now, let's say that "foo" instead calls "fn bar(_: T) -> T". When passing the object itself, the lifetime is largely decided/decide-able by "bar".
- IshKebab 1y agoThat's true of mutable references too though isn't it? In fact lots of people have suggested they should really have been called "exclusive references", since you can actually mutate some objects through non-exclusive references (called "interior mutability" normally).
- tayo42 1y agoRefcel didn't work? Or rc?
- rfoo 1y agoSlapping Rc<T> over something that could be clearly uniquely owned is a sign of very poorly designed lifetime rules / system. And yes, for now async Rust is full of unnecessary Arc<T> and is very poorly made.
- zozbot234 1y agoIf the thread can be dropped while the buffer is "owned" by the kernel io-uring facilities (to be given back when the operation completes) that's not "unique" ownership. The existing Rc/Arc<T> may be overkill for that case, but something very much like it will still be needed.
- pornel 1y agoThis can be done with exclusively owned objects. That's how io_uring abstractions work in Rust – you give your (heap allocated) buffer to a buffer pool, and get it back when the operation is done. &mut references are exclusive and non-copyable, so the hot potato approach can even be used within their scope. But the problem in Rust is that threads can unwind/exit at any time, invalidating buffers living on the stack, and io_uring may use the buffer for longer than the thread lives. The borrow checker only checks what code is doing, but doesn't have power to alter runtime behavior (it's not a GC after all), so it only can prevent io_uring abstractions from getting any on-stack buffers, but has no power to prevent threads from unwinding to make on-stack buffer safe instead.
- alfiedotwtf 1y agoIn my universe, `let` wouldn’t exist… instead there would only be 3 ways to declare variables: 1. global my_global_var: GlobalType = … 2. heap my_heap_var: HeapType = … 3. stack my_stack_var: StackType = … Global types would need to implement a global trait to ensure mutual exclusion (waves hands). So by having the location of allocation in the type itself, we no longer have to do boxing mental gymnastics
- IX-103 1y agoDoesn't Rust do this? `let` is always on the stack. If you want to allocate on the heap then you need a Box. So `let foo = Box::new(MyFoo::default ())` creates a Box on the stack that points to a MyFoo on the heap. So MyFoo is a stack type and Box<MyFoo> is a heap type. Or do you think there is value in defining MyFooStack and MyFooHeap separately to support both use cases?