Skip to content

Commit d12dda8

Browse files
committed
Update to reflect discussion and recent thinking
1 parent 0023db2 commit d12dda8

File tree

1 file changed

+39
-22
lines changed

1 file changed

+39
-22
lines changed

text/0000-rustfmt-stability.md

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Some changes would clearly be non-breaking (e.g., performance improvements) or c
1212

1313
The goal is for formatting to only ever change when a user deliberately upgrades Rustfmt. For a project using Rustfmt, the version of Rustfmt (and thus the exact formatting) can be controlled by some artifact which can be checked-in to version control; thus all project developers and continuous integration will have the same formatting (until Rustfmt is explicitly upgraded).
1414

15-
I propose two possible solutions: versioning is handled by Rustfmt (by formatting according to the rules of previous versions), or versioning is handled by Cargo (by treating Rustfmt as a dev dependency).
15+
I propose handling versioning internally in Rustfmt, by formatting according to the rules of previous versions and having users opt-in to breaking changes.
1616

1717

1818
# Motivation
@@ -29,13 +29,13 @@ Rustfmt has a programmatic API (the RLS is a major client), the usual backwards
2929
# Guide-level explanation
3030
[guide-level-explanation]: #guide-level-explanation
3131

32-
If you're using Rustfmt, formatting won't change without an explicit upgrade (i.e., a major version increase). This covers all formatting to all code, subject to the following restriction:
32+
If you're using Rustfmt, formatting won't change without an explicit upgrade (i.e., a major version increase). This covers all formatting to all code, subject to the following restrictions:
3333

3434
* using the default options
3535
* code must compile using stable Rust
3636
* formatting with Rustfmt is *error free*
3737

38-
'Formatting is *error free*' means that when Rustfmt formats a program, it only changes the formatting of the program and does not make any significant changes. I.e., does not change the semantics of the program or any names. This caveat means that we can fix bugs in Rustfmt where the changed formatting cannot affect any users, because previous versions of Rustfmt could cause an error.
38+
'Formatting is *error free*' means that when Rustfmt formats a program, it only changes the formatting of the program and does not change the semantics of the program or any names. This caveat means that we can fix bugs in Rustfmt where the changed formatting cannot affect any users, because previous versions of Rustfmt could cause an error.
3939

4040
Furthermore, any program which depends on Rustfmt and uses it's API, or script that runs Rustfmt in any stable configuration will continue to build and run after an update.
4141

@@ -98,6 +98,7 @@ A common way to use Rustfmt is in an editor via the RLS. The RLS is primarily di
9898

9999
In this section we define what constitutes different kinds of breaking change for Rustfmt.
100100

101+
101102
### API breaking change
102103

103104
A change that could cause a dependent crate not to build, could break a script using the executable, or breaks specification-level formatting compatibility. A formatting change in this category would be frustrating even for occasional users.
@@ -112,19 +113,23 @@ Examples:
112113

113114
Any API breaking change will require a major version increment. Changes to formatting at this level (other than bug fixes) will require an amendment to the specification RFC
114115

115-
Open question: when and how to update the Rust distribution with such a change. However, I don't expect this to be an issue in the short-run, since we will avoid such changes.
116+
An API breaking change would cause a semver major version increment.
116117

117118
### Major formatting breaking change
118119

119-
Any change which would change the formatting of code which was previously correctly formatted. In particular when run on CI, any change which would cause `rustfmt --write-mode=check` to fail where it previously succeeded.
120+
Any change which would change the formatting of code which was previously correctly formatted. In particular when run on CI, any change which would cause `rustfmt --check` to fail where it previously succeeded.
120121

121122
This only applies to formatting with the default options. It includes bug fixes, and changes at any level of detail or to any kind of code.
122123

124+
A major formatting breaking change would cause a semver minor version increment, however, users would have to opt-in to the change.
125+
123126

124127
### Minor formatting breaking change
125128

126129
These are changes to formatting which cannot cause regressions for users using default options and stable Rust. That is any change to formatting which only affects formatting with non-default options or only affects code which does not compile with stable Rust.
127130

131+
A minor formatting breaking change would cause a semver minor version increment.
132+
128133

129134
### Non-breaking change
130135

@@ -139,27 +144,37 @@ Examples:
139144
* stabilising an option or variant of an option
140145
* performance improvements or other non-formatting, non-API changes
141146

142-
Such changes only require a patch version increment; the Rust distribution can be freely updated.
147+
Such changes only require a patch version increment.
143148

144149

145-
## Proposals
150+
## Proposal
146151

147-
Dealing with API breaking changes and non-breaking changes is trivial so won't be covered here. I have two proposals, one based on managing versioning internally using an 'edition' system, and one based on managing versions externally in Cargo.
148-
149-
### Internal handling
152+
Dealing with API breaking changes and non-breaking changes is trivial so won't be covered here.
150153

151154
* Stabilise the `required_version` option (probably renamed)
152-
* API changes are a major version increment; major and minor formatting changes are a minor formatting increment, BUT major changes are opt-in with a version number, e.g, using rustfmt 1.4, you get 1.0 formatting unless you specify `required_version = 1.4`
153-
* rustfmt supports all versions on the same major version number and the last previous one (an LTS release - open question: is this necessary?), e.g., rustfmt 2.4 would support `1.19, 2.0, 2.1, 2.2, 2.3, 2.4`
155+
* API changes are a major version increment; major and minor formatting changes are a minor formatting increment, BUT major formatting changes are opt-in with a version number, e.g, using rustfmt 1.4, you get 1.0 formatting unless you specify `required_version = 1.4`
156+
* Each published rustfmt supports formatting using all minor versions of the major version number, e.g., rustfmt 2.4 would support `2.0, 2.1, 2.2, 2.3, 2.4`.
157+
* Even if the API does not change, we might periodically (and infrequently) publish a major version increment to end support for old formatting versions.
158+
* The patch version number is not taken into account when choosing how to format.
159+
* if you want older versions, you must use Cargo to get the older version of Rustfmt and build from source.
154160
* internally, `required_version` is supported just like other configuration options
155-
* alternative - the edition version could be specified in Cargo.toml as a dev-dependency/task and passed to rustfmt
161+
* alternative: the version could be specified in Cargo.toml as a dev-dependency/task and passed to rustfmt
156162

157-
This approach adds complexity to Rustfmt (but perhaps no worse than current options). Every bug fix or improvement would need to be gated on either the `required_version` or an unstable option.
158163

159-
On the other hand, all changes are internal to Rustfmt and we don't require changes to any other tools. Users would rarely need to install or build different versions of Rustfmt. Non-breaking changes get to all users quickly.
164+
### Publishing
165+
166+
Rustfmt can be used via three major channels: via Cargo, via Rustup, and via the RLS. To ensure there are no surprises between the different distribution mechanisms, we will only distribute published versions, i.e., we will not publish a Git commit which does not correspond to a release via Rustup or the RLS.
167+
168+
# Drawbacks
169+
[drawbacks]: #drawbacks
170+
171+
We want to make sure Rustfmt can evolve and stability guarantees make that more complex. However, it is certainly a price worth paying, and we should just ensure that we can still make forwards progress on Rustfmt.
160172

161173

162-
### External handling
174+
# Rationale and alternatives
175+
[alternatives]: #alternatives
176+
177+
## External handling
163178

164179
* Major formatting changes cause a major version increment, minor formatting changes cause a minor version increment
165180
- QUESTION - how do we distinguish API breaking changes from major formatting changes?
@@ -175,14 +190,16 @@ On the other hand, all changes are internal to Rustfmt and we don't require chan
175190
Rustfmt would have to maintain a branch for every supported release and backport 'necessary' changes. Hopefully we would minimise these - there should be no security fixes, and most bug fixes would be breaking. Anyone who expects to get changes to unstable Rustfmt should be using the latest version, so we shouldn't backport unstable changes. I'm sure there would be some backports though.
176191

177192

178-
# Drawbacks
179-
[drawbacks]: #drawbacks
193+
## Rationale for choosing internal handling
180194

181-
We want to make sure Rustfmt can evolve and stability guarantees make that more complex. However, it is certainly a price worth paying, and we should just ensure that we can still make forwards progress on Rustfmt.
195+
The internal handling approach adds complexity to Rustfmt (but no worse than current options). Every bug fix or improvement would need to be gated on either the `required_version` or an unstable option.
182196

197+
On the other hand, all changes are internal to Rustfmt and we don't require changes to any other tools. Users would rarely need to install or build different versions of Rustfmt. Non-breaking changes get to all users quickly.
183198

184-
# Rationale and alternatives
185-
[alternatives]: #alternatives
199+
It is not clear how to integrate the external handling with Rustup, which is how many users get Rustfmt. It would also be complicated to manage branches and backports under the external handling approach.
200+
201+
202+
## Other alternatives
186203

187204
Two alternative are spelled out above. A third alternative is to version according to semver, but not make any special effort to constrain breaking changes. This would result in either slowing down development of Rustfmt or frequent breaking changes. Due to the nature of distribution of rustfmt, that would make it effectively impossible to use in CI.
188205

@@ -198,4 +215,4 @@ Other formatters (Gofmt, Clang Format) have not dealt with the stability/version
198215
# Unresolved questions
199216
[unresolved]: #unresolved-questions
200217

201-
We need to decide on internal or external handling of versioning. There are several open questions for the latter (highlighted inline).
218+
Whether we want to specify the version in Cargo instead of/as well as in rustfmt.toml.

0 commit comments

Comments
 (0)