Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

the Drop example isn't good enough #1843

Open
correabuscar opened this issue May 12, 2024 · 1 comment
Open

the Drop example isn't good enough #1843

correabuscar opened this issue May 12, 2024 · 1 comment

Comments

@correabuscar
Copy link

correabuscar commented May 12, 2024

in https://doc.rust-lang.org/rust-by-example/trait/drop.html
there's no example showing how drop() happens in case of inner fields:
the parent struct's drop() gets executed first then the fields' are executed in declaration order(instead of reverse order aka Unlike for structs, local variables are dropped in reverse order) of which they were created.

This seems like an important distinction, even though it's covered in the docs. But not important enough that I figure out how to mod the example via a PR, so this is left for someone far more capable than I :)

This examplifies the above (playground]:

struct Inner {
    name: String,
}

impl Inner {
    fn new(name: &str) -> Self {
        println!("Creating Inner: {}", name);
        Inner {
            name: name.to_string(),
        }
    }
}

impl Drop for Inner {
    fn drop(&mut self) {
        println!("Dropping Inner: {}", self.name);
    }
}

struct Parent {
    inner1: Inner,
    inner2: Inner,
}

impl Parent {
    fn new() -> Self {
        println!("Creating Parent");
        Parent {
            inner1: Inner::new("Inner 1i"),
            inner2: Inner::new("Inner 2i"),
        }
    }
    fn new2(i1:Inner, i2:Inner) -> Self {
        println!("Creating Parent");
        Parent {
            inner1: i1,
            inner2: i2,
        }
    }
}

impl Drop for Parent {
    fn drop(&mut self) {
        println!("Dropping Parent");
    }
}

fn main() {
    {
        println!("Example1:");
        {
            let mut parent = Parent::new();
        }
        println!("Example2:");
        {
            //the order of the inners matter when dropping them
            let inner1 = Inner::new("New Inner 1");
            let inner2 = Inner::new("New Inner 2");
            //but it doesn't matter which parent/inners were created first
            //in either of the 3 examples in main()
            let mut parent = Parent::new2(inner1, inner2);
        }
        println!("Example3:");
        let mut parent = Parent::new();
        // Set inner fields to new Inner objects
        parent.inner1 = Inner::new("New Inner 1");
        parent.inner2 = Inner::new("New Inner 2");
        println!("Exiting inner scope");
    }
    println!("Exiting main scope");
}

output:

Example1:
Creating Parent
Creating Inner: Inner 1i
Creating Inner: Inner 2i
Dropping Parent
Dropping Inner: Inner 1i
Dropping Inner: Inner 2i
Example2:
Creating Inner: New Inner 1
Creating Inner: New Inner 2
Creating Parent
Dropping Parent
Dropping Inner: New Inner 1
Dropping Inner: New Inner 2
Example3:
Creating Parent
Creating Inner: Inner 1i
Creating Inner: Inner 2i
Creating Inner: New Inner 1
Dropping Inner: Inner 1i
Creating Inner: New Inner 2
Dropping Inner: Inner 2i
Exiting inner scope
Dropping Parent
Dropping Inner: New Inner 1
Dropping Inner: New Inner 2
Exiting main scope
@correabuscar
Copy link
Author

correabuscar commented May 12, 2024

And if you wanted to implement your own drop() for the inner fields, how would you do it? Maybe this way?(playground) (or is there another, perhaps more idiomatic one?)

use std::mem::ManuallyDrop;

struct Inner {
    name: String,
}

impl Inner {
    fn new(name: &str) -> Self {
        println!("Creating Inner: {}", name);
        Inner {
            name: name.to_string(),
        }
    }
}

impl Drop for Inner {
    fn drop(&mut self) {
        println!("Dropping Inner: {}", self.name);
    }
}

struct Parent {
    inner1: ManuallyDrop<Inner>,
    inner2: ManuallyDrop<Inner>,
}

impl Parent {
    fn new() -> Self {
        println!("Creating Parent");
        Parent {
            inner2: ManuallyDrop::new(Inner::new("Inner 2i")),
            inner1: ManuallyDrop::new(Inner::new("Inner 1i")),
        }
    }
    fn new2(i1:Inner, i2:Inner) -> Self {
        println!("Creating Parent");
        Parent {
            inner1: ManuallyDrop::new(i1),
            inner2: ManuallyDrop::new(i2),
        }
    }
}

impl Drop for Parent {
    fn drop(&mut self) {
        println!("Dropping Parent, manually dropping begins:");
        //Dropping these in reverse order (manually), just for kicks
        unsafe { std::mem::ManuallyDrop::drop(&mut self.inner2) };
        unsafe { std::mem::ManuallyDrop::drop(&mut self.inner1) };
        //technically can still see/use them hereafter still, but it's UB?!
        println!("Dropping Parent done manually dropping");
    }
}

fn main() {
    {
        println!("Example1:");
        {
            let _parent = Parent::new();
        }
        println!("Example2:");
        {
            //the order of the inners creation does not matter when dropping them, they're dropped in field order
            let inner2 = Inner::new("New Inner 2");
            let inner1 = Inner::new("New Inner 1");
            //but it doesn't matter which parent/inners were created first
            //in either of the 3 examples in main()
            let _parent = Parent::new2(inner1, inner2);
        }
        println!("Example3:");
        let mut parent = Parent::new();
        // Set inner fields to new Inner objects
        parent.inner2 = ManuallyDrop::new(Inner::new("New Inner 2"));
        parent.inner1 = ManuallyDrop::new(Inner::new("New Inner 1"));
        println!("Exiting inner scope");
    }
    println!("Exiting main scope");
}

output:

Example1:
Creating Parent
Creating Inner: Inner 2i
Creating Inner: Inner 1i
Dropping Parent, manually dropping begins:
Dropping Inner: Inner 2i
Dropping Inner: Inner 1i
Dropping Parent done manually dropping
Example2:
Creating Inner: New Inner 2
Creating Inner: New Inner 1
Creating Parent
Dropping Parent, manually dropping begins:
Dropping Inner: New Inner 2
Dropping Inner: New Inner 1
Dropping Parent done manually dropping
Example3:
Creating Parent
Creating Inner: Inner 2i
Creating Inner: Inner 1i
Creating Inner: New Inner 2
Creating Inner: New Inner 1
Exiting inner scope
Dropping Parent, manually dropping begins:
Dropping Inner: New Inner 2
Dropping Inner: New Inner 1
Dropping Parent done manually dropping
Exiting main scope

EDIT: Notice how the old ones that got replaced directly got leaked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant