- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13.9k
Description
One of unsafe trait DerefPure's preconditions is that Self's Deref impl is "well-behaved". std::borrow::Cow currently always implements DerefPure, but Cow<B>'s Deref impl can call arbitrary user code in <B::Owned as Borrow<B>>::borrow that may not be "well-behaved".
I tried this (safe) code:
#![feature(deref_patterns)]
use std::borrow::{Cow, Borrow};
struct Weird<T: ?Sized = [()]>(bool, T);
struct WeirdOwned {
    val: std::cell::Cell<bool>,
}
impl Borrow<Weird> for WeirdOwned {
    fn borrow(&self) -> &Weird {
        let val = self.val.get();
        self.val.set(!val);
        if val { &Weird(true, []) } else { &Weird(false, []) }
    }
}
impl ToOwned for Weird {
    type Owned = WeirdOwned;
    fn to_owned(&self) -> WeirdOwned {
        WeirdOwned {
            val: false.into(),
        }
    }
}
fn main() {
    let x: Cow<Weird> = Cow::Owned(WeirdOwned { val: true.into() });
    match x {
        deref!(Weird(false, _)) | deref!(Weird(true, _)) => println!("a"),
        _ => println!("b"),
    }
    // prints b
    
    let x: Cow<Weird> = Cow::Owned(WeirdOwned { val: true.into() });
    match x {
        deref!(Weird(false, _) | Weird(true, _)) => println!("a"),
        _ => println!("b"),
    }
    // prints a
}I expected to see this happen: Either this shouldn't compile (because Cow<Weird>'s Deref impl is not "well-behaved"), or the first match should also print "a".
Instead, this happened: Having two separate deref! patterns joined by an or-pattern behaves differently than an or-pattern in a deref! pattern. IIUC currently there is no soundness issue because deref patterns do not interact with exhaustiveness checking, but if the first match didn't require the blanket _ arm and considered deref!(Weird(false, _)) | deref!(Weird(true, _)) exhaustive, then the exhibited behavior would be a soundness issue.
One possible resolution might be to restrict Cow's DerefPure impls to just Cow<T: Sized>, Cow<str>, Cow<[T]>, and other types that the stdlib fully controls the ToOwned/Borrow/Deref impls of.
-unsafe impl<B: ?Sized + ToOwned> DerefPure for Cow<'_, B> where B::Owned: Borrow<B> {}
+unsafe impl<T: Clone> DerefPure for Cow<'_, T>{}
+unsafe impl DerefPure for Cow<'_, str>{}
+unsafe impl<T: Clone> DerefPure for Cow<'_, [T]>{}incorrect suggestion
Moved this into a hidden block because this would not work with Cow<T: Sized>, since T::Owned = T, which probably doesn't implement Deref<Target = T> + DerefPure.
One possible resolution might be to add "
Borrow<Self::Target>/BorrowMut<Self::Target>are well-behaved if implemented" toDerefPure's preconditions, and then changeCow's impl as follows:-unsafe impl<B: ?Sized + ToOwned> DerefPure for Cow<'_, B> where B::Owned: Borrow<B> {} +unsafe impl<B: ?Sized + ToOwned> DerefPure for Cow<'_, B> where B::Owned: Borrow<B> + Deref<Target = B> + DerefPure {}Then,
Cow<Weird>would not implementDerefPure, becauseWeirdOwneddoes not implementDerefPure. This would still allowCow<str>, andCow<[T]>(sinceStringandVec<T>implementDerefPure), but this would not work withCow<T: Sized>, sinceT::Owned = T, which might not implementDerefPure, and probably doesn't implementDeref<Target = T>.Note that just adding
B::Owned: DerefPureis not sufficient, since you could haveWeird::Owned = Box<WeirdOwned>, andBox: DerefPure.
Meta
rustc --version --verbose:
rustc 1.86.0-nightly (649b995a9 2025-01-22)
binary: rustc
commit-hash: 649b995a9febd658b2570160703dff6fdc038ab2
commit-date: 2025-01-22
host: x86_64-unknown-linux-gnu
release: 1.86.0-nightly
LLVM version: 19.1.7
@rustbot label +F-deref-patterns +requires-nightly