diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 712c3af..23191dd 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -21,6 +21,7 @@ - [Doc alias policy](./policy/doc-alias.md) - [Safety comments policy](./policy/safety-comments.md) - [Reviewing target-specific code](./policy/target-code.md) + - [Why the standard library uses extension traits](./policy/extension-traits.md) - [Tricky situations]() - [Drop and `#[may_dangle]`](./tricky/may-dangle.md) diff --git a/src/development/feature-lifecycle.md b/src/development/feature-lifecycle.md index ce51bce..dc015e5 100644 --- a/src/development/feature-lifecycle.md +++ b/src/development/feature-lifecycle.md @@ -35,7 +35,7 @@ Keep the following points in mind during the discussion: - An overly specific API does not cover all common use cases, and may require further API changes in the future to accomodate these use cases. - An alternative that should *always* be considered is simply adding this feature via a third party crate. This is even possible when adding new methods to standard library types by using extension traits. - In the case of "convenience" functions which are simply shorthands for something that is already possible with existing APIs, the cost of extending the standard library surface should be weighed against the ergonomic impact of the new functions. - - For example, too many convenience methods on a type makes nagivating the documentation more difficult. + - For example, too many convenience methods on a type makes navigating the documentation more difficult. - Additionally, consider whether this method is likely to be deprecated in the future if a language-level improvement makes it unnecessary. The library team itself is not directly involved in this discussion, but individual members may comment to provide feedback. If significant changes have occurred since the ACP, another one may be proposed at this point to have the design validated by the library API team. diff --git a/src/development/stabilization.md b/src/development/stabilization.md index 5adf6c3..e5fcb01 100644 --- a/src/development/stabilization.md +++ b/src/development/stabilization.md @@ -15,7 +15,7 @@ If you're unsure if a feature is ready for stabilization the first step should b ## Stabilization Report -Once a feature is ready for stabilization the first step of the FCP process is writing a stabilization report. Stabilization reports are not mandatory but they are heavily encouraged, and may be mandated by library API team members if they feel it necessary. The purpose of stabilization reports is to help reviewers more quickly make decisions and to simplify the process of documenting stabilized APIs in release notes. Stabilization reports consist of three primary sections, a implementation history, an API summary, and an experience report. +Once a feature is ready for stabilization the first step of the FCP process is writing a stabilization report. Stabilization reports are not mandatory but they are heavily encouraged, and may be mandated by library API team members if they feel it necessary. The purpose of stabilization reports is to help reviewers more quickly make decisions and to simplify the process of documenting stabilized APIs in release notes. Stabilization reports consist of three primary sections, an implementation history, an API summary, and an experience report. The **Implementation History** section should summarize the initial discussion during the implementation PR, every change that has been made to the feature since the initial implementation, all issues that were raised during the lifetime of the feature, and how they were resolved. diff --git a/src/policy/extension-traits.md b/src/policy/extension-traits.md new file mode 100644 index 0000000..b69f28c --- /dev/null +++ b/src/policy/extension-traits.md @@ -0,0 +1,22 @@ +# Why the standard library uses extension traits (and not `cfg`-guarded items) + +A common pattern in the standard library is to put target-specific methods into +extension traits, rather than providing them as `cfg`-guarded methods directly +on objects themselves. For example, the many extension traits in +[`std::os::unix::prelude`](https://doc.rust-lang.org/std/os/unix/prelude/index.html) +provide UNIX-specific methods on standard types. + +The standard library could, instead, provide these methods directly on the +standard types, guarded by `#[cfg(unix)]`. However, it does not do so, and PRs +adding `cfg`-guarded methods are often rejected. + +Providing these methods via extension traits forces code to explicitly use +those extension traits in order to access the methods. This, effectively, +requires code to declare whether it depends on target-specific functionality, +either because the code is target-specific, or because it has appropriately +`cfg`-guarded code for different targets. Without these extension traits, code +could more easily use target-specific functionality "accidentally". + +This policy may change in the future if Rust develops better mechanisms for +helping code explicitly declare its portability, or lack of portability, before +accessing target-specific functionality. diff --git a/src/policy/unsafe-preconditions.md b/src/policy/unsafe-preconditions.md new file mode 100644 index 0000000..dcbb365 --- /dev/null +++ b/src/policy/unsafe-preconditions.md @@ -0,0 +1,9 @@ +# Runtime checks for preconditions of `unsafe fn` + +When possible, a debug assertion for the preconditions of an `unsafe fn` should be added inside the body of said function, before the implementation exploits the precondition. + +The compiler supports two kinds of debug assertions. Those that branch on `cfg(debug_assertions)` such as `debug_assert!` or `debug_assert_nounwind!` will be compiled out of the standard library distributed by rustup. Such checks are still valuable to add because they can be used by external tools like [cargo-careful](https://crates.io/crates/cargo-careful) or [cargo-fuzz](https://crates.io/crates/cargo-fuzz), users of `-Zbuild-std` or just our own CI (because it enables both optimizations and debug assertions). + +When it does not impose a significant compile-time burden, debug assertions should be implemented by branching on `intrinsics::debug_assertions()`. That intrinsic is only lowered after monomorphization, so calls to that intrinsic which appear in public and `#[inline]` or generic functions will be enabled by users in builds that enable debug assertions. We have a macro for automating the best use pattern for this intrinsic, `intrinsics::assert_unsafe_precondition!`. This macro shifts all the actual checking logic into a monomorphic and `#[inline(never)]` function, which ensures that the check and error reporting logic is compiled once instead of again and again for each monomorphization that uses the check. + +`assert_unsafe_precondition!` also uses `const_eval_select` internally so that it is only enabled at runtime. When you need a runtime-only check (for example, if your precondition is about pointer alignment) but the compile-time overhead of the branch and call that it expands to is too significant, it is fine to write `#[cfg(debug_assertions)] assert_unsafe_precondition!`.