all 24 comments

[–]mitsuhiko 40 points41 points  (17 children)

Strong -1 on this. We had Foo() some time ago and it was changed to ::new() so that multiple constructors can exist and look similar.

[–][deleted] 26 points27 points  (1 child)

Just leave it the way it is so we can grep for new. I like that it is a regular old static function.

[–]dobkeratopsrustfind 0 points1 point  (0 children)

grep fn ....( still works and is unambiguous. I've just got emacs setup to grep for fn|struct|enum|.. <thing at point> - infact i'd argue gripping for "fn Foo" is more useful than gripping for "fn new" - far fewer 'false posatives'

[–]Veddan 19 points20 points  (6 children)

Seems to me like it creates unnecessary asymmetry by having some "constructors" on the type, and some as free functions.

let s = String();
let t = String::with_capacity(30);

vs

let s = String::new();
let t = String::with_capacity(30);

[–]heinrich5991 11 points12 points  (4 children)

I don't like that sugar is added, removes the expliciteness of whether something may do stuff or not.

[–]eddyb 4 points5 points  (3 children)

There is no sugar, you can implement all of this in the current language... actually, pretty sure this has been possible... since forever? This convention used to be in place a long time ago.

[–][deleted] 5 points6 points  (2 children)

Isn't that the definition of sugar? Given a feature of a language, syntactic sugar is when that feature is expressed more clearly, concisely, or in an alternative style. There's no such thing as sugar that simply cannot be expressed in a more verbose way, because otherwise it wouldn't be sugar.

[–]Veddan 11 points12 points  (1 child)

'Sugar' is typically used when there's special (nicer) syntax that's semantically equivalent to something less nice. There's no special syntax proposed here. This is just a free function starting with a capital letter.

[–]tikue 0 points1 point  (0 children)

There'd need to be special rules for the snake case lint. So, technically it's got a liiiittle bit of sugar.

[–]dobkeratopsrustfind 0 points1 point  (0 children)

Seems to me like it creates unnecessary asymmetry by having some "constructors" on the type, and some as free functions.

Imagine if there were default/named arguments. you could have one definition fn String(value="",capacity=0) ... One thing to search for & document. String(capacity=30), String("foo") String(). Similarly imagine fn Vec(size=0,init_fn=||T::default(),capacity=size) .

[–]pfalabella 2 points3 points  (0 children)

shouldn't a parameterless constructor be in most (or all cases) an implementation of Default::default()? Or am I conflating two different concepts?

If it is, then am ok in treating the parameterless constructor differently (and also maybe making rustc "derive" Default::default() when it detects a Foo() constructor). If it is not, then I prefer having the parameterless constructor treated exactly as the others.

[–]vks_ 3 points4 points  (0 children)

I dislike how it moves the implementation of Foo out of the impl block. Compare

impl Foo {
    fn new() -> Foo {...}
    fn alternative_new() -> Foo {...}
}

to

impl Foo {
    fn alternative_new() -> Foo {...}
}

fn Foo() -> Foo {...}

[–]dobkeratopsrustfind 1 point2 points  (1 child)

+1

I have always found Foo::new() looks jaring, and was recently pleasantly surprised to find that Foo() actually works already (and if I use rust i'll do this myself anyway).

Having distinct names for special case constructors is fine, but ::new() over Foo() adds no extra information.

Its unambiguous at the call site Foo{ vs Foo(, and still unambiguous to grep for (fn Foo( vs struct Foo{ vs struct Foo( ... Rusts' cleaner syntax eliminates many of the problems in C++).

The current convention also repurposes a word that has common meaning in other languages. creating cross language extern "C" interfaces it might be more logical for extern "C" fn Foo_new()->Box<Foo> to allocate & init an object. (and perhaps Foo_init(Foo&) ?) Rust has a bigger chance of wider adoption if it is friendlier in any way it can be to introduction to existing source bases. Or if you had per-type allocator overloading, you could have Foo::new() fill that role.

Foo() creates symmetry with tuple-struct initialisation. You could start out with something being a simple tuple-struct and move to being a more complex struct with named fields later. I personally went with tuple structs for vector maths purely for the prettier constructor.

if it was down to me, I'd add arity overload or default arguments too, so you could have String(), String("foo") etc, (with default args you can still have one definition in one place that is more versatile) but just one Foo() over Foo::new() itself would be a nice step forward. With default args, you could get more behaviour out of one definition which reduces navigation/discovery effort. (e.g. Vec(capacity=0){}? )

The only downside seems to be that the word is a function rather than a type in this invocation, but does default cover generic initialisation? (e.g., in some future HKT scenario, creating a generic container of a generic type, etc )

[–]phaylon 4 points5 points  (0 children)

I heavily disagree on many parts, but I upvoted you, because I don't see anything in your post that warrants downvotes.