[idea] Allow borrowing twice in unsafe area

I often encounter borrowing twice issues (Usually, hold the self.member variable and call other self methods after borrowing), and usually I have to modify the code beyond recognition to bypass borrowing checks, even if I am confident that there will be no problems with both borrowings. I hope the unsafe keyword can block two borrowing checks to simplify code implementation.

related: Closure idea: Relax capture rules for closures

I think this could be better solved in other ways.

You mentioned that you want this feature because

Assuming your other self methods don't need access to self.member (which would cause unsoundness), this would be better served by either of these features:

  1. Private partial borrowing

    Partial borrowing would let your other self methods to specify that they don't need access to self.member, telling the borrow checker that the borrows are disjoint.

  2. Private macro methods

    The idea with these is to have functions that don't specify argument types and the borrow checker can see through them, checking that you use the borrows in a disjoint way.


Now, if you wanted to use unsafe code, you certainly can, but not in the way you think. Even unsafe is not allowed to ever create two mutable references to the same thing. Instead, use *mut raw pointers everywhere. (And don't forget to mark functions taking a raw pointer as argument as unsafe!)

Also be wary of the sign on the wall:

It has been 0 days since someone tried to use unsafe to bypass the borrow checker and failed

Why I don't think your original proposal can work

The unsafe keyword doesn't change semantics

In Rust, using the unsafe keyword does not disable the borrow checker and I think this is a very good design choice: it makes writing unsafe still safer than C, beacuse you can focus on whatever unsafe operation you're using without needing to think about all other borrowing rules.

If you want to explicitly bypass the borrow checker, you can still do it explicitly with raw pointers.

&mut references could escape the block

If the compiler would allow you to create two mutable references, many APIs would become unsound, and would mean that calling any function with any mutable reference from an unsafe block would be UB, because it would lose the guarantee that the &mut provides: uniqueness.

It may be solved by the view typed. View types for Rust · baby steps

unsafe does not turn off the borrow checker, and that is by design:

5 Likes

:robot: Beep boop. I see you're trying to mutably borrow twice using unsafe. Okay!

let mut a = 0;
// SAFETY: hahaha
let b = unsafe { &mut *(&mut a as *mut i32) };
a += 1;
*b += 1;
println!("{}, {}", a, b);
1 Like

This should be resolved by the borrow regions (or similar) feature.

You can do a bit better by using &UnsafeCell<T>, since the borrow checker will still prevent the reference from outliving the borrow, and you only need to worry about aliasing the references to the inner data.

That can be packaged into a safe abstraction specific to whatever restriction you are using to guarantee safety. You could, for example, split a &mut Foo into ViewA<'_> and ViewB<'_> which wrap a &UnsafeCell<Foo> and give &mut-access to some disjoint subsets of fields. Unfortunately you lose most of the ergonomics of the native reference types.

2 Likes