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

RFC - Allow Drop types in statics/const functions #1440

Merged
merged 7 commits into from Apr 22, 2016
70 changes: 70 additions & 0 deletions text/0000-drop-types-in-const.md
@@ -0,0 +1,70 @@
- Feature Name: `drop_types_in_const`
- Start Date: 2016-01-01
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

Allow types with destructors to be used in `static` items and in `const` functions, as long as the destructor never needs to run in const context.

# Motivation
[motivation]: #motivation

Some of the collection types do not allocate any memory when constructed empty (most notably `Vec`). With the change to make leaking safe, the restriction on `static` items with destructors
is no longer required to be a hard error (as it is safe and accepted that these destructors may never run).

Allowing types with destructors to be directly used in `const` functions and stored in `static`s will remove the need to have
runtime-initialisation for global variables.

# Detailed design
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section should explore different implementation strategies as well. E.g. do we use .ctors and .dtors sections (that’s what c++ does) which allows for life-before-main (which we’ve been very reluctant about allowing) or do we employ scheme similar to lazy_static! (initialize on first use)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may not have been clear enough here - This is just to use the current const structure (no life before main, no life after it) to do compile-time construction of objects that support it.

[design]: #detailed-design

- Lift the restriction on types with destructors being used in statics.
- `static`s containing Drop-types will not run the destructor upon program/thread exit.
- (Optionally adding a lint that warn about the possibility of resource leak)
- Alloc instantiating structures with destructors in constant expressions,
- Continue to prevent `const` items from holding types with destructors.
- Allow `const fn` to return types with destructors.
- Disallow constant expressions which would result in the destructor being called (if the code were run at runtime).

## Examples
Assuming that `RwLock` and `Vec` have `const fn new` methods, the following example is possible and avoids runtime validity checks.

```rust
/// Logging output handler
trait LogHandler: Send + Sync {
// ...
}
/// List of registered logging handlers
static S_LOGGERS: RwLock<Vec< Box<LogHandler> >> = RwLock::new( Vec::new() );
```

Disallowed code
```rust
static VAL: usize = (Vec::<u8>::new(), 0).1; // The `Vec` would be dropped
const EMPTY_BYTE_VEC: Vec<u8> = Vec::new(); // `const` items can't have destructors

const fn sample(_v: Vec<u8>) -> usize {
0 // Discards the input vector, dropping it
}
```

# Drawbacks
[drawbacks]: #drawbacks

Destructors do not run on `static` items (by design), so this can lead to unexpected behavior when a type's destructor has effects outside the program (e.g. a RAII temporary folder handle, which deletes the folder on drop). However, this can already happen using the `lazy_static` crate.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This drawback is not necessary. I.e. we could use .dtors section which would execute all the necessary destructors for statics once we exit from main.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Life after main has the same problems as life before main, destruction order in particular. (The same set of problems apply to thread locals.)


# Alternatives
[alternatives]: #alternatives

- Runtime initialisation of a raw pointer can be used instead (as the `lazy_static` crate currently does on stable)
- On nightly, a bug related to `static` and `UnsafeCell<Option<T>>` can be used to remove the dynamic allocation.
- Both of these alternatives require runtime initialisation, and incur a checking overhead on subsequent accesses.
- Leaking of objects could be addressed by using C++-style `.dtors` support
- This is undesirable, as it introduces confusion around destructor execution order.

# Unresolved questions
[unresolved]: #unresolved-questions

- TBD