Open Divy1211 opened 2 weeks ago
Since there's no backtrace I was confused as to where the error came from.
We can, in principle, use Location to track where borrows happen, like how std's debug_refcell
feature does it. But our borrow tracking is already fairly complicated, and tracking borrows would also be complicated. I'm reluctant to add more complexity to it.
I guess we wouldn't need to store any state where the borrows happen at least? We could just slap #[track_caller]
on a bunch of methods and then use the location to make errors a little bit easier to understand?
I guess we wouldn't need to store any state where the borrows happen at least?
We would need to store it inside the borrow checker implementation. See https://github.com/PyO3/pyo3/compare/main...mejrs:pyo3:debug_pyref for example. then we'd also need to be clever about how to track and untrack where shared borrows start and end.
Suggestion
My suggestion is to improve the error message generated by
Bound<_', T>
when the runtime borrowing rules are violated to look something like this:If a shared reference exists and a
borrow_mut
was attempted:Perhaps an additional help text could also be included?:
If a mutable borrow exists and
borrow
is attempted:Motivation
TL;DR: Just providing a better indicator of where the error comes from, to provide better debugging direction.
Story Time:
While trying to write some code, I had a struct containing
Arc<PyObject>
s andArc<RwLock<<T>>
s in Rust and when I tried to callbound_obj.extract::<Struct>()
, I got an error that simply saidAlready mutably borrowed
. Since there's no backtrace I was confused as to where the error came from. Not being super familiar with the semantics ofArc
s andRwLock
s I thought the error was coming from trying to clone them, instead of the fact thatextract
immutably borrows theBound<'_, T>
. It was only after I implementedClone
manually (and other unsuccessful debugging) I found that the failure occurs beforeclone
is even called.After hand tracing my value back to its source, I realised that a name (the mutable reference I made earlier) not being live does not trigger an implicit
drop()
like I thought, and that I needed to call it manually (that's where the suggestion for the extra help comes from, because I was initially creating nested scopes until I recalleddrop
exists.)Implementation
I'd be happy to try and implement this! I think this is simple enough and I'd love to be able to gain some familiarity with PyO3's code itself =)