You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: text/0000-rustfmt-stability.md
+39-22Lines changed: 39 additions & 22 deletions
Original file line number
Diff line number
Diff line change
@@ -12,7 +12,7 @@ Some changes would clearly be non-breaking (e.g., performance improvements) or c
12
12
13
13
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).
14
14
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.
16
16
17
17
18
18
# Motivation
@@ -29,13 +29,13 @@ Rustfmt has a programmatic API (the RLS is a major client), the usual backwards
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:
33
33
34
34
* using the default options
35
35
* code must compile using stable Rust
36
36
* formatting with Rustfmt is *error free*
37
37
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.
39
39
40
40
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.
41
41
@@ -98,6 +98,7 @@ A common way to use Rustfmt is in an editor via the RLS. The RLS is primarily di
98
98
99
99
In this section we define what constitutes different kinds of breaking change for Rustfmt.
100
100
101
+
101
102
### API breaking change
102
103
103
104
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:
112
113
113
114
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
114
115
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.
116
117
117
118
### Major formatting breaking change
118
119
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.
120
121
121
122
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.
122
123
124
+
A major formatting breaking change would cause a semver minor version increment, however, users would have to opt-in to the change.
125
+
123
126
124
127
### Minor formatting breaking change
125
128
126
129
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.
127
130
131
+
A minor formatting breaking change would cause a semver minor version increment.
132
+
128
133
129
134
### Non-breaking change
130
135
@@ -139,27 +144,37 @@ Examples:
139
144
* stabilising an option or variant of an option
140
145
* performance improvements or other non-formatting, non-API changes
141
146
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.
143
148
144
149
145
-
## Proposals
150
+
## Proposal
146
151
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.
150
153
151
154
* 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.
154
160
* 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
156
162
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.
158
163
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.
160
172
161
173
162
-
### External handling
174
+
# Rationale and alternatives
175
+
[alternatives]: #alternatives
176
+
177
+
## External handling
163
178
164
179
* Major formatting changes cause a major version increment, minor formatting changes cause a minor version increment
165
180
- 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
175
190
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.
176
191
177
192
178
-
# Drawbacks
179
-
[drawbacks]: #drawbacks
193
+
## Rationale for choosing internal handling
180
194
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.
182
196
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.
183
198
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
186
203
187
204
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.
188
205
@@ -198,4 +215,4 @@ Other formatters (Gofmt, Clang Format) have not dealt with the stability/version
198
215
# Unresolved questions
199
216
[unresolved]: #unresolved-questions
200
217
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