Summary
A static method is written today with static between the parameter list and the return type: fn Temperature.to_fahrenheit(c: double) static: double. This proposal moves it to the front, with the declaration’s other modifiers: static fn Temperature.to_fahrenheit(c: double): double, or pub static fn ... / public static fn .... 2.3 accepts both and warns on the old place; 3.0 removes it (breaking-change entry B-20).
Motivation
Whether a method takes self is the first thing a reader needs to know about it, and the parser needs it too. Today both find out only after reading the whole parameter list, from a word wedged between ) and : where nothing else goes. In front, static reads like pub does: a property of the declaration, stated before the declaration. Java, C#, C++ and Swift’s static func all put it there.
Design
struct Temperature {}
static fn Temperature.to_fahrenheit(c: double): double {
return c * 9.0 / 5.0 + 32.0;
}
pub static fn Temperature.freezing(): double { return 0.0; }
static fn<T> Box.of(v: T): Box<T> { return Box<T> { v: v }; }
- The modifiers come in this order: visibility (
puborpublic), thenstatic, thenfn.static pub fnis a syntax error with aFix:that reorders them. - A generic static method writes its type parameters on
fn, as today:static fn<T> Box.of(...). - Every rule about static methods is unchanged: no
self, called asT::m(...)and never through an instance,newcannot be static (E046),staticon a function that is not a method is E053. - 2.3: the old place keeps compiling and gets a warning (allow-by-default when it lands, then warn) with a
Fix:that moves the word. Writingstaticin both places is an error. - 3.0:
staticafter the parameter list is an error with the sameFix:.turf migrate 3.0moves every one.
Constitution check
- I. Safe by default, unsafe by declaration. Not affected.
- II. Native performance, with costs you can read. Not affected.
- III. The compiler is the authority. The warning, the error and the fix are the parser’s, with stable codes.
- IV. Nothing crosses a boundary implicitly. Not affected. The visibility word stays first, where a reader looks for it.
- V. A few general mechanisms, not many special cases. Modifiers of a declaration go in one place, in front of it, instead of one of them living after the parameters.
- VI. It works when you install it. Not affected.
- VII. Stability is a promise. The new spelling is additive in 2.3 (
staticat the start of a declaration is E103 today); the old one is removed only in 3.0, after warning, with a mechanical migration. - VIII. A claim is not true until it is demonstrated. See Verification.
The Non-Goals hold: no borrow checker, no no-runtime targets, no AI in the compilation path.
Migration
fn T.m(args) static: R becomes static fn T.m(args): R, keeping any pub/public in front. The 2.3 warning’s Fix: and turf migrate 3.0 do it; the standard library and the examples move in 2.3.
Verification
- 2.3: each form above compiles to the same program as its old spelling (identical IR); both places at once is an error;
static pub fnis an error with the reordering fix; the warning fires on the old place only. - 3.0: the old place is an error; the compatibility corpus is migrated by
turf migrate 3.0and then passes.
Drawbacks
- Every static method in existing code moves; the migration is mechanical, but it touches many lines.
Alternatives
fn static T.m(). Considered; rejected by the owner in favour of the Java-like order, which keeps every modifier in front offn.- Keep the current place. Rejected: the word is found late by both the reader and the parser.
Unresolved questions
None.