rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.42k stars 12.6k forks source link

`PhantomData`, mutable references and `drop` interaction unclear #100573

Open crepererum opened 2 years ago

crepererum commented 2 years ago

I tried this code:

use std::marker::PhantomData;

struct S;

impl S {
    fn create_ref(&mut self) -> Ref<'_> {
        Ref {
            _phantom: PhantomData::default(),
            _tester: DropTester,
        }
    }

    fn use_it(&self) {
        println!("use_it")
    }
}

struct Ref<'a> {
    _phantom: PhantomData<&'a mut ()>,
    _tester: DropTester,
}

// uncomment this to "fix" the bug:
//
// impl<'a> Drop for Ref<'a> {
//     fn drop(&mut self) {}
// }

struct DropTester;

impl Drop for DropTester {
    fn drop(&mut self) {
        println!("DropTester::drop")
    }
}

fn main() {
    let mut s = S;
    let r = s.create_ref();
    s.use_it();
}

(Playground link)

I expected to see this happen:

It fails to compile because we hold a mutable reference to s and call use_it.

Instead, this happened:

It compiles and the output is:

use_it
DropTester::drop

Meta

Happens on both stable (currently 1.63.0) and nightly (currently 1.65.0-nightly (2022-08-14 801821d1560f84e4716f))

Explanation

You might wonder why someone would build such a construct: In the actual code the DropTester is a parking_lot ArcMutexGuard that provides access to some internal field (owned by S) and I've tried to use lifetimes to avoid that someone would hold the reference wrapper (Ref) while using some other methods on S.

SkiFire13 commented 2 years ago

AFAIK this is intended, the lifetime is only required to outlive the last use of the value containing it, and in your example the PhantomData is not used anymore after s.create_ref(). This see through fields unless the type implements Drop, which is why implementing Drop for Ref changes the behaviour.

crepererum commented 2 years ago

This see through fields unless the type implements Drop

Is this documented somewhere? (I guess so, but I'm failing to find it or to know what to search for)