-
-
Save jpastuszek/0471b276e6ec4fe187c4731b1581a303 to your computer and use it in GitHub Desktop.
Drop and pahntom ref unsound drop order
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | |
} |
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());
}
Explanation about temp at the end of expression:
https://www.reddit.com/r/rust/comments/6dynjz/psa_temporaries_in_final_expression_in_block_live/
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The key here is that
PhantomData
is used separate ofRaii
to "ensure" thatFoo
outlivesConnection
whileRaii
has aDrop
impl that needs to run BEFOREConnection
was dropped. Compiler does not put this two things together as: do not outliveConnection
&& hasDrop
impl.