It seems that a static object's drop method will never get called. Is that true?
struct S(u8);
impl Drop for S {
fn drop(&mut self) {
println!("drop {}",self.0);
}
}
static G : S = S(0);
fn main() {
S(1);
}
It seems that a static object's drop method will never get called. Is that true?
struct S(u8);
impl Drop for S {
fn drop(&mut self) {
println!("drop {}",self.0);
}
}
static G : S = S(0);
fn main() {
S(1);
}
https://doc.rust-lang.org/reference/items/static-items.html
Static items do not call
drop
at the end of the program.
To clarify, it is not the object that is static but the variable. If you have a "mutable static" you can very well drop the object that was created statically and have an object created at run-time end up not being dropped:
use parking_lot::{Mutex, const_mutex};
struct S(u8);
impl Drop for S {
fn drop(&mut self) {
println!("drop {}",self.0);
}
}
static G: Mutex<S> = const_mutex(S(0));
fn main() {
*G.lock() = S(1);
}
Interesting. What is the rationale for this? Does C++ have similar behavior?
I think it's because it's (very) hard to do it:
"right" (no way to trigger UB, independently of the order of drops, multiple threads, and things like that;
cross-platform;
with negligible runtime cost.
In practice, you can try you own attempt at this, using https://docs.rs/ctor's #[dtor]
, and by wrapping the static
in an Option
:
use ::ctor::dtor;
use ::parking_lot::{const_rwlock, RwLock};
struct S { /* ... */ }
impl Drop for S {
fn drop (self: &'_ mut Self)
{
// this could try to access `G`; guarding about all these cases is tough
println!("drop");
}
}
static G: RwLock<Option<S>> = const_rwlock(Some({
#[dtor]
fn drop_on_shutdown ()
{
*G.write() = None;
}
S { /* ... */ }
}));
Note that a far simpler option, if you are not dealing with multi-threaded code, is to use thread_local!
statics, which Rust will "try to drop" (best effort):
struct S { /* ... */ }
impl Drop for S {
fn drop (self: &'_ mut Self)
{
// this could try to access `G`; guarding about all these case is tough
let s = format!("drop from thread: {:?}\0", ::std::thread::current().id());
unsafe {
// May be called on main program shutdown,
// when `println!` isn't available
::libc::puts(s.as_bytes().as_ptr().cast());
}
}
}
thread_local! {
static G: S = S { /* ... */ };
}
fn main ()
{
G.with(|_| ());
let _ = ::std::thread::spawn(|| {
G.with(|_| ());
}).join();
}
That being said, note that for a thread_local!
to be instanced, you need to access the value using .with()
, since the thread-locals are lazily initialized.
Implement a macro with an API identical to thread_local!
's but with the semantics of a global var:
droppable_static! {
static G: S = S { ... }
}
// Users may call `.with(|g| ...)` where the closure would have
// a signature of `impl FnOnce(&'_ S) -> _`
// And `G` is dropped on program shutdown.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.