Eliminate redundancy in constructor-like methods


#1

Rust is full of functions that behave exactly like constructors in other languages:

struct String {
    vec: Vec<u8>,
}

impl String {
    /// Creates a new string buffer initialized with the empty string.
    pub fn new() -> String {
        String {
            vec: Vec::new(),
        }
    }
}

Would it be possible to introduce (optional) syntax that unifies the return type and the initializer? Like so:

impl String {
    /// Creates a new string buffer initialized with the empty string.
    pub fn new() -> String {
        vec: Vec::new(),
    }
} 

This syntax would activate when the parser sees field initializers instead of statements inside a function block. It wouldn’t be dependent on name or context.

It reduces visual noise and makes it clear that the function is creating a new instance.

I don’t think you could have statements in the initializer, but you can always code it like a regular function. Visual noise/redundancy in this case isn’t an issue since the initializer block and the function signature aren’t on adjacent lines:

pub fn new() -> String {
    let buffer: Vec<u8> = Vec::new();
    
    String {
        vec: buffer,
    }
}

#2

I think this is a neat sugar and flows naturally, so +1 from me.


#3

In total you save two braces and a type name at the cost of making the language much more complex. Note: Until the parser sees the braches of Vec::new() it can’t determine whether it’s a function body or a magic initializer.


#4

The line

vec: Vec::new()

is invalid within a function block but valid in an initializer. As soon as the parser sees either that or a statement/expression, it knows what kind of block this is. That doesn’t seem like much added complexity to me, requiring only a simple lookahead before declaring the type of the block, but I’m far from an expert in parser design.


#5

This syntax would interfere with type ascription, which is something we’ve wanted to do forever but haven’t gotten around to. On balance I think I’d rather have type ascription.


#6

I think the worst thing about the Rust constructors is that you have to repeat the type name two times. Allowing the use of Self in implementations would resolve this issue.


#7

I think this could be solved if we allowed = after the function declaration:

pub fn new() = String { vec : vec![] }

This would be equivalent (type-inferred) to:

pub fn new() -> String = String { vec : vec![] }

which is new syntax equivalent to our currently valid

pub fn new() -> String { String { vec : vec![] } }

While at it, I’d also love to see this defined as

pub new : () -> String = String { vec : vec![] }