From 0b967045d12d29302fbf5d97684b477af9f081c1 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 16 Mar 2026 18:26:27 -0500 Subject: [PATCH] docs: Better document breaking change policy and recommended usage For a while there has been an informal policy about what is and isn't allowed regarding breaking changes, but it hasn't been written down anywhere. This is an attempt to start to give our users some more concrete expectations, documented in the "Stability Expectations" section. I expect this will be updated as we think of more cases. I additionally added a "Usage Recommendations" section that is meant to give actionable suggestions about how to avoid breakage and other problems. Closes: https://github.com/rust-lang/libc/issues/5018 --- CONTRIBUTING.md | 15 +----- README.md | 8 --- src/lib.rs | 129 +++++++++++++++++++++++++++++++++++++++++++++++- src/unix/mod.rs | 3 ++ 4 files changed, 132 insertions(+), 23 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f43cda673d37b..411f90387e76d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -97,20 +97,7 @@ We have two automated tests running on ## Breaking change policy -Sometimes an upstream adds a breaking change to their API e.g. removing outdated -items, changing the type signature, etc. And we probably should follow that -change to build the `libc` crate successfully. It's annoying to do the -equivalent of semver-major versioning for each such change. Instead, we mark the -item as deprecated and do the actual change after a certain period. The steps -are: - -1. Add `#[deprecated(since = "", note="")]` attribute to the item. - - The `since` field should have a next version of `libc` (e.g., if the current - version is `0.2.1`, it should be `0.2.2`). - - The `note` field should have a reason to deprecate and a tracking issue to - call for comments (e.g., "We consider removing this as the upstream removed - it. If you're using it, please comment on #XXX"). -2. If we don't see any concerns for a while, do the change actually. +See `src/lib.rs` for details. ## Supported target policy diff --git a/README.md b/README.md index f8b2d58313402..b05a52d113ecb 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,6 @@ Add the following to your `Cargo.toml`: libc = "0.2" ``` -## Features - -* `std`: by default `libc` links to the standard library. Disable this feature - to remove this dependency and be able to use `libc` in `#![no_std]` crates. - -* `extra_traits`: all `struct`s implemented in `libc` are `Copy` and `Clone`. - This feature derives `Debug`, `Eq`, `Hash`, and `PartialEq`. - ## Rust version support The minimum supported Rust toolchain version is currently **Rust 1.65**. diff --git a/src/lib.rs b/src/lib.rs index 6e6577fda3e8d..a77fd45ef38da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,131 @@ -//! libc - Raw FFI bindings to platforms' system libraries +//! Raw FFI bindings to platforms' system libraries. +//! +//! # Usage Recommendations +//! +//! `libc` takes a differe +//! +//! - *Never* construct a `libc` struct with `MaybeUninit::uninit()`, initialize it, then call +//! `assume_init`. Many structures have padding fields or may gain fields in the future, and +//! it is far too easy to end up calling `assume_init` on partially initialized data. +//! +//! Instead, use `MaybeUninit::zeroed()` or the `Default` implementations that are slowly being +//! added. Alternatively, access fields only via raw pointer without ever using `assume_init`. +//! +//! - Avoid relying on the exact value of constants or the exact length of arrays, as they may +//! change across `libc` versions. That is, if `libc` contains code like: +//! +//! +//! ```ignore +//! const IFNAMSIZ: usize = 16; +//! +//! pub struct ifreq { +//! pub ifr_name: [c_char; IFNAMSIZ], +//! // ... +//! } +//! ``` +//! +//! Then avoid writing code like: +//! +//! ```ignore +//! // Bad assumption that `IFNAMSIZ` will be 16 forever +//! fn foo(ifr_name: [c_char; 16]) { /* ... */ } +//! +//! fn bar(ifr: ifreq) { +//! foo(ifr.ifr_name); +//! } +//! ``` +//! +//! Instead, use `[c_char; IFNAMSIZ]` to specify the type, or just `&[c_char]`. +//! +//! Along the same lines, if you write code along the lines of `assert_eq!(libc::ELAST, 97)`, +//! expect that there may be a release where this starts to fail. +//! +//! - Do not name `__c_anonymous_*` types anywhere, which exist to represent anonymous fields in +//! C. For example, FreeBSD defines: +//! +//! ```c +//! struct filestat { +//! int fs_type; +//! // ... +//! struct { struct filestat stqe_next; } next; +//! }; +//! ``` +//! +//! Which is represented in `libc` as: +//! +//! ```ignore +//! struct filestat { +//! fs_type: c_int, +//! // ... +//! next: __c_anonymous_filestat, +//! } +//! +//! struct __c_anonymous_filestat { stqe_next: *mut filestat } +//! ``` +//! +//! Accessing `some_filestat.next.stqe_next` is completely fine, but `__c_anonymous_filestat` +//! should not be used anywhere (e.g. in a function signature). This is done to permit `libc` to +//! switch to anonymous fields if the feature is ever added to Rust. +//! +//! - Be aware of deprecation warnings. These are used as a way to migrate necessary API changes. +//! +//! # Cargo Features +//! +//! - `std`: by default `libc` assumes that the standard library contains link directives necessary +//! to use the APIs in this crate. If `std` is disabled, `libc` will emit directives necessary to +//! link the necessary C libraries. +//! +//! This feature is slated for removal in `libc` 1.0. The intention is that no-std users of +//! `libc` should use their own `#[link]` attributes, `rustc-link-lib` build script directives, +//! or `-l` arguments for only the system libraries they need to link, rather than `libc` +//! possibly linking more than is needed or available. If you are using `libc` without the `std` +//! feature, consider starting to add link directives now for a smoother 1.0 transition. +//! +//! - `extra_traits`: all types in `libc` implement `Clone`, `Copy`, and `Debug`. The +//! `extra_traits` feature adds `Eq`, `Hash`, and `PartialEq`. +//! +//! This feature is expected to be removed in libc 1.0. Libraries should instead hash or check +//! equality of only needed fields. +//! +//! - The features `const-extern-function`, `align`, and `use_std` are all deprecated. +//! +//! # Stability Expectations +//! +//! Due to `libc`'s position in the ecosystem, it can effectively never publish semver-breaking +//! releases. However, the API that `libc` binds changes _all the time_; sometimes in ways that +//! are harmless, sometimes in ways that are technically API-breaking for C users but unlikely +//! to be noticed (e.g. removing deprecated API), and sometimes in ways that are nonbreaking in +//! C but translate to breaking changes in Rust (e.g. changing integer size). `libc` tries to +//! strike a balance but all of this means that unfortunately, `libc` must occasionally ship +//! changes within a semver-compatible release that are technically semver-breaking. +//! +//! The following are examples of changes that fall into this category: +//! +//! - Fields are added to a struct that is currently exhaustive. +//! - Fields with names such as `padding` or `reserved` change type or are removed. +//! - The length of an array type changes. +//! - A struct field (with available padding) is changed from `int` to `long`. +//! +//! In general, `libc` aims to follow platform API changes, even when this means changes that are +//! user-visible in Rust. There are a few guidelines used here: +//! +//! - Adding struct fields is not considered breaking, nor is changing fields named `reserved`, +//! `padding`, or similar. This is because users are expected to use field-by-field +//! initialization. +//! - Changing type aliases, values of constants, or array lengths is not considered breaking. +//! - If the platform libc has accepted breakage on the C side (typically in the form of removing +//! old API), the `libc` crate will follow suit. +//! - Where possible, `#[deprecated(...)]` will be used to warn about changes before applying them. +//! Alternative mitigations may be considered. +//! - Potentially breaking changes will be well-identified in release notes. +//! - Beyond this, public API is not expected to change on Tier 1 targets. Tier 2 targets have +//! relaxed API stability requirements, and API stability is not enforced on tier 3 targets. +//! +//! While this section seems scary, keep in mind that it is meant to cover worst-case scenarios. In +//! practice, breakage is rare and following the above-discussed [Best Practices](#best-practices) +//! means that most `libc` users will never encounter a problem. + #![crate_name = "libc"] #![crate_type = "rlib"] #![allow( diff --git a/src/unix/mod.rs b/src/unix/mod.rs index ff440d5a13f1c..854091c8a5290 100644 --- a/src/unix/mod.rs +++ b/src/unix/mod.rs @@ -430,6 +430,9 @@ extern "C" { pub static in6addr_any: in6_addr; } +// FIXME(1.0): We want to remove these directives and instead expect that no-std users add their +// own link configuration when required, rather than unconditionally linking everything that may +// possibly be needed. cfg_if! { if #[cfg(any( target_os = "l4re",