Skip to content

Instantly share code, notes, and snippets.

@jpastuszek
Forked from rust-play/playground.rs
Last active May 7, 2020 12:22
Show Gist options
  • Save jpastuszek/0471b276e6ec4fe187c4731b1581a303 to your computer and use it in GitHub Desktop.
Save jpastuszek/0471b276e6ec4fe187c4731b1581a303 to your computer and use it in GitHub Desktop.
Drop and pahntom ref unsound drop order
use std::marker::PhantomData;
struct Connection(u32);
impl Connection {
fn statement(&self) -> Statement {
Statement(Raii(self.0), PhantomData)
}
}
impl Drop for Connection {
fn drop(&mut self) {
println!("dropping Connection({})", self.0)
}
}
struct Raii(u32);
impl Drop for Raii {
fn drop(&mut self) {
println!("dropping Raii({})", self.0)
}
}
struct Statement<'c>(Raii, PhantomData<&'c u32>);
/* this should not be required because Raii implements Drop but this would prevent bad example from compiling.
impl Drop for Statement<'_> {
fn drop(&mut self) { }
}
*/
struct Foo<'c>(&'c Connection, Statement<'c>);
impl<'c> Foo<'c> {
fn new(connection: &'c Connection) -> Foo<'c> {
Foo(connection, connection.statement())
}
fn mu(&mut self) -> u32 {
((self.1).0).0
}
}
fn main() {
let c = || {
let connection = Connection(7);
/*
dropping Raii(42)
dropping Connection(42)
Err(Some(42))
*/
let mut foo = Foo::new(&connection);
foo.mu()
};
println!("{:?}", c());
let c = || {
let connection = Connection(42);
/*
dropping Connection(42)
dropping Raii(42)
Err(Some(42))
*/
// This should not compile: `foo` dropped here while still borrowed
Foo::new(&connection).mu()
};
println!("{:?}", c());
}
@jpastuszek
Copy link
Author

jpastuszek commented May 5, 2020

The key here is that PhantomData is used separate of Raii to "ensure" that Foo outlives Connection while Raii has a Drop impl that needs to run BEFORE Connection was dropped. Compiler does not put this two things together as: do not outlive Connection && has Drop impl.

@jpastuszek
Copy link
Author

Better way to fix this is to put PahntomData<'c> into Raii:

use std::marker::PhantomData;

struct Connection(u32);

impl Connection {
    fn statement(&self) -> Statement {
        Statement(Raii(self.0, PhantomData))
    }
}

impl Drop for Connection {
    fn drop(&mut self) {
        println!("dropping Connection({})", self.0)
    }
}

struct Raii<'c>(u32, PhantomData<&'c u32>);

impl Drop for Raii<'_> {
    fn drop(&mut self) {
        println!("dropping Raii({})", self.0)
    }
}

struct Statement<'c>(Raii<'c>);

struct Foo<'c>(&'c Connection, Statement<'c>);

impl<'c> Foo<'c> {
    fn new(connection: &'c Connection) -> Foo<'c> {
        Foo(connection, connection.statement())
    }

    fn mu(&mut self) -> u32 {
        ((self.1).0).0
    }
}

fn main() {
    let c = || {
        let connection = Connection(7);

        /*
            dropping Raii(42)
            dropping Connection(42)
            Err(Some(42))
        */
        let mut foo = Foo::new(&connection);
        foo.mu()
    };
    
    println!("{:?}", c());

    let c = || {
        let connection = Connection(42);
        /*
            dropping Connection(42)
            dropping Raii(42)
            Err(Some(42))
        */
        // This should not compile: `foo` dropped here while still borrowed
        Foo::new(&connection).mu()
    };
    
    println!("{:?}", c());
}

@jpastuszek
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment