Skip to content

Crate versioning strategy

Stephen M. Coakley edited this page Oct 6, 2026 · 2 revisions

This page is meant to serve as documentation explaining how the curl-rust project handles crate versions, since it may not be obvious at first glance, and it might be considered a little bit out of the ordinary for most Rust projects. Clarifying this should help both users and contributors.

Crates overview

The curl-rust project provides safe bindings to the libcurl C API. These bindings hold safety as a top priority, but the second priority is to maintain an API that is as similar to the libcurl API as possible without breaking general Rust-isms. However, this means that some things that are possible with the libcurl API are not possible with the safe API (usually due to lifetimes). To still support these other use cases, the raw unsafe bindings are held in the curl-sys crate, while the safe bindings are in the curl crate, which consumes curl-sys as a dependency.

curl and curl-sys are versioned separately; they are not version-locked. It is often possible to use curl with many different versions of curl-sys. In practice, curl and curl-sys are often released together with new features, so their version numbers do tend to be similar to each other, but this is not a guarantee.

libcurl versions and linking

curl-sys supports both dynamic linking and static linking to libcurl. This is for two reasons:

  1. Some people really want static linking of curl, and others really want dynamic linking. So we offer a crate feature which leaves that choice up to the user, rather than us making that choice for everyone.
  2. If a system-wide curl is not available, we fall back toward static linking, as a preference toward at least having a successful build rather than failing the build.

When linking statically, we usually link to a bundled distribution of the libcurl source that is included within releases of curl-sys. This is explicitly opted into via the static-curl crate feature. Since therefore, curl-sys releases include a bundled release of curl within it, we include the version of that bundled release in the semver build metadata section as a helpful moniker/reminder of which version of curl is bundled with which version of curl-sys, like this: 0.4.76+curl-8.10.1. The curl version is added after the + character which denotes build metadata by SemVer. This is not used for any version comparisons, so you could not, for example, specify a version range to prevent a minor bump in curl version as specified in that build metadata.

This means that, whenever we update the version of libcurl that is bundled with curl-sys, the build metadata in the crate version must also be updated to match the new version that is bundled. This is currently a manual process.

Keeping the libcurl version as build metadata is also helpful because it provides us wiggle room to release multiple versions of curl-sys that fix some aspect of the bindings or add new bindings without necessarily changing which version of libcurl we are bundling.

However, this build metadata can also be a little misleading, because if you are not using static linking, and instead using dynamic linking, then the bundled version of libcurl is not used at all. In that scenario, the actual version of libcurl in use could be just about anything, as it will vary at runtime depending on what version is installed on an end-user's machine.

Numbering scheme

We follow Cargo's subset of Semantic Versioning. Since the latest versions of both curl and curl-sys start with 0., this means effectively for the purposes of API compatibility, the versioning scheme is treated like 0.MAJOR.MINOR, with new features and bugfixes incrementing the right-most number. Breaking changes warrant an increase in the middle number, or releasing version 1.0.0.

In practice, we are very cautious and conservative about breaking changes, since curl and curl-sys are somewhat foundational crates in the Rust ecosystem, and releasing a breaking change could cause a lot of headache for downstream developers of many projects. This is especially true of curl-sys, which by necessity declares links in its manifest which has a side-effect of causing Cargo to not allow two versions of curl-sys to exist simultaneously in the final binary. Not that you would want that anyway if it were possible, since both curl-rust and libcurl have global state.

Upgrading libcurl

We do not consider upgrading the bundled libcurl version to be a breaking change of curl-sys, so long as libcurl itself does not ship with any breaking API changes. However, this may mean that new versions of libcurl may increase their minimum required versions of system dependencies at build time, such as C compiler versions, libc, or its dependencies such as OpenSSL.

This may have the unfortunate consequence of causing a project that formerly compiled on an older system to no longer compile on that same system. While we understand that this can be frustrating and is not an ideal situation, we feel that this downside is a smaller negative impact to the Crates.io ecosystem at large than the alternative, which would be to bump our major version every time libcurl increases its requirements of any system dependency.

Minimum supported libcurl versions

We do aim to keep curl-sys relatively up-to-date with the latest upstream libcurl version, at least for the version bundled with the crate. However, since you can dynamically link to any libcurl version, we aim to keep the bindings of curl-sys itself compatible with older libcurl versions wherever possible, since users have a diverse set of versions of libcurl installed on their systems.

It actually is not very often that libcurl releases entirely new APIs, so this is usually not difficult to maintain. Most new features are added via new constants for curl_easy_setopt, which is harmless when linking to an older version of libcurl. At worst case, you might try to invoke curl_easy_setopt with a new option that the linked libcurl doesn't understand and it will return a recoverable error at runtime.

When new functions are added, we usually gate those bindings in curl-sys using crate features that include the version of libcurl that introduced the new API, like this: poll_7_68_0. These are disabled by default, allowing users to opt-in to the new API if they need it, knowing it increases their project's minimum libcurl version, while other projects that don't need the API are able to link to older libcurl versions if they so choose.