Eliminate redundancy in constructor-like methods

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,
    }
}

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

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.

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.

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.

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.

1 Like

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![] }
2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.