Skip to content

Commit b60c168

Browse files
committed
Merge branch 'no-wildcard-deps' of https://github.com/sfackler/rfcs
2 parents 0599646 + 10d7e82 commit b60c168

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed

text/0000-no-wildcard-deps.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
- Feature Name: N/A
2+
- Start Date: 2015-07-23
3+
- RFC PR:
4+
- Rust Issue:
5+
6+
# Summary
7+
8+
A Cargo crate's dependencies are associated with constraints that specify the
9+
set of versions of the dependency with which the crate is compatible. These
10+
constraints range from accepting exactly one version (`=1.2.3`), to
11+
accepting a range of versions (`^1.2.3`, `~1.2.3`, `>= 1.2.3, < 3.0.0`), to
12+
accepting any version at all (`*`). This RFC proposes to update crates.io to
13+
reject publishes of crates that have compile or build dependencies with
14+
a wildcard version constraint.
15+
16+
# Motivation
17+
18+
Version constraints are a delicate balancing act between stability and
19+
flexibility. On one extreme, one can lock dependencies to an exact version.
20+
From one perspective, this is great, since the dependencies a user will consume
21+
will be the same that the developers tested against. However, on any nontrival
22+
project, one will inevitably run into conflicts where library A depends on
23+
version `1.2.3` of library B, but library C depends on version `1.2.4`, at
24+
which point, the only option is to force the version of library B to one of
25+
them and hope everything works.
26+
27+
On the other hand, a wildcard (`*`) constraint will never conflict with
28+
anything! There are other things to worry about here, though. A version
29+
constraint is fundamentally an assertion from a library's author to its users
30+
that the library will work with any version of a dependency that matches its
31+
constraint. A wildcard constraint is claiming that the library will work with
32+
any version of the dependency that has ever been released *or will ever be
33+
released, forever*. This is a somewhat absurd guarantee to make - forever is a
34+
long time!
35+
36+
Absurd guarantees on their own are not necessarily sufficient motivation to
37+
make a change like this. The real motivation is the effect that these
38+
guarantees have on consumers of libraries.
39+
40+
As an example, consider the [openssl](https://crates.io/crates/openssl) crate.
41+
It is one of the most popular libraries on crates.io, with several hundred
42+
downloads every day. 50% of the [libraries that depend on it](https://crates.io/crates/openssl/reverse_dependencies)
43+
have a wildcard constraint on the version. None of them can build against every
44+
version that has ever been released. Indeed, no libraries can since many of
45+
those releases can before Rust 1.0 released. In addition, almost all of them
46+
them will fail to compile against version 0.7 of openssl when it is released.
47+
When that happens, users of those libraries will be forced to manually override
48+
Cargo's version selection every time it is recalculated. This is not a fun
49+
time.
50+
51+
Bad version restrictions are also "viral". Even if a developer is careful to
52+
pick dependencies that have reasonable version restrictions, there could be a
53+
wildcard constraint hiding five transitive levels down. Manually searching the
54+
entire dependency graph is an exercise in frustration that shouldn't be
55+
necessary.
56+
57+
On the other hand, consider a library that has a version constraint of `^0.6`.
58+
When openssl 0.7 releases, the library will either continue to work against
59+
version 0.7, or it won't. In the first case, the author can simply extend the
60+
constraint to `>= 0.6, < 0.8` and consumers can use it with version 0.6 or 0.7
61+
without any trouble. If it does not work against version 0.7, consumers of the
62+
library are fine! Their code will continue to work without any manual
63+
intervention. The author can update the library to work with version 0.7 and
64+
release a new version with a constraint of `^0.7` to support consumers that
65+
want to use that newer release.
66+
67+
Making crates.io more picky than Cargo itself is not a new concept; it
68+
currently [requires several items](https://github.com/rust-lang/crates.io/blob/8c85874b6b967e1f46ae2113719708dce0c16d32/src/krate.rs#L746-L759) in published crates that Cargo will not:
69+
70+
* A valid license
71+
* A description
72+
* A list of authors
73+
74+
All of these requirements are in place to make it easier for developers to use
75+
the libraries uploaded to crates.io - that's why crates are published, after
76+
all! A restriction on wildcards is another step down that path.
77+
78+
Note that this restriction would only apply to normal compile dependencies and
79+
build dependencies, but not to dev dependencies. Dev dependencies are only used
80+
when testing a crate, so it doesn't matter to downstream consumers if they
81+
break.
82+
83+
This RFC is not trying to prohibit *all* constraints that would run into the
84+
issues described above. For example, the constraint `>= 0.0.0` is exactly
85+
equivalent to `*`. This is for a couple of reasons:
86+
87+
* It's not totally clear how to precisely define "reasonable" constraints. For
88+
example, one might want to forbid constraints that allow unreleased major
89+
versions. However, some crates provide strong guarantees that any breaks will
90+
be followed by one full major version of deprecation. If a library author is
91+
sure that their crate doesn't use any deprecated functionality of that kind of
92+
dependency, it's completely safe and reasonable to explicitly extend the
93+
version constraint to include the next unreleased version.
94+
* Cargo and crates.io are missing tools to deal with overly-restrictive
95+
constraints. For example, it's not currently possible to force Cargo to allow
96+
dependency resolution that violates version constraints. Without this kind of
97+
support, it is somewhat risky to push too hard towards tight version
98+
constraints.
99+
* Wildcard constraints are popular, at least in part, because they are the
100+
path of least resistance when writing a crate. Without wildcard constraints,
101+
crate authors will be forced to figure out what kind of constraints make the
102+
most sense in their use cases, which may very well be good enough.
103+
104+
# Detailed design
105+
106+
The prohibition on wildcard constraints will be rolled out in stages to make
107+
sure that crate authors have lead time to figure out their versioning stories.
108+
109+
In the next stable Rust release (1.4), Cargo will issue warnings for all
110+
wildcard constraints on build and compile dependencies when publishing, but
111+
publishes those constraints will still succeed. Along side the next stable
112+
release after that (1.5 on December 11th, 2015), crates.io be updated to reject
113+
publishes of crates with those kinds of dependency constraints. Note that the
114+
check will happen on the crates.io side rather than on the Cargo side since
115+
Cargo can publish to locations other than crates.io which may not worry about
116+
these restrictions.
117+
118+
# Drawbacks
119+
120+
The barrier to entry when publishing a crate will be mildly higher.
121+
122+
Tightening constraints has the potential to cause resolution breakage when no
123+
breakage would occur otherwise.
124+
125+
# Alternatives
126+
127+
We could continue allowing these kinds of constraints, but complain in a
128+
"sufficiently annoying" manner during publishes to discourage their use.
129+
130+
This RFC originally proposed forbidding all constraints that had no upper
131+
version bound but has since been pulled back to just `*` constraints.

0 commit comments

Comments
 (0)