Need for controlling drop order of fields

Note that for this case, using an Option (or another mem::replace-like take pattern) can replace the unsafe-ty:

Option
struct Worker {
    /// Wrapped in an `Option` to be able to drop it earlier (on `Drop`)
    sender: Option< Sender<Item> >,

    thread: jthread::Handle,
}

impl Worker {
    fn sender (self: &'_ mut Self) -> &'_ mut Sender<Item>
    {
        self.sender.as_mut().expect("Already dropped `sender`??")
    }
}

impl Drop for Worker {
    fn drop (self: &'_ mut Self)
    {
        // drop the `sender` before the rest / the `thread` handle.
        drop(self.sender.take());
    }
}

mem::replace

struct Worker {
    sender: Sender<Item>,
    // ...
    thread: jthread::Handle,
}

impl Drop for Worker {
    fn drop (self: &'_ mut Self)
    {
        // drop the `sender` before the rest / the `thread` handle.
        drop(::core::mem::replace(&mut self.sender, channel::new(...).0));
    }
}

That is, I feel like the moment this is perceived as a "drop earlier" kind of issue, rather than a drop later, the take pattern becomes more intuitive / idiomatic.


Regarding the OP itself, I would say that a comment on either sender or thread reminding people to pay unusual attention to declaration order in that it suddenly is semantically meaningful is a quite decent solution :smile:

  • in that regard, a procedural macro to enforce field orders would solve this issue and be quite trivial to implement:

    #[derive(FieldOrderSensitive)] // Hints at something non-trivial going on here
    struct Worker {
        sender: Sender<Item>,
    
        #[assert_declared_after(.sender, reason = "needs to be dropped after it")]
        thread: jthread::Handle,
    }
    
5 Likes