Skip typing `self.` each time in a method and instead use `.`

Can we refer to members inside methods using dot .member instead of writing self.member each time ?

This would be ambiguous, for various reasons.

For instance:

if cond {
    a()
} else {
    b()
}
.method();

Is that an if-else calling a() or b() followed by a call to self.method(), or is that a call to .method() on the result of the if-else? Today it's unambiguously the latter.

17 Likes

no it is not ambiguous because it is not valid to write:

if cond {
    a()
} else {
    b()
}
self.method();

I don't want to interpret any dot to self. but only where self. is valid.

It is valid to write that.

15 Likes

my wrong ! it is indeed valid if the result of if is () ! however not a blocking thing it can be still parsed as it is currently

Rust requires brackets on if in part to avoid the "dangling else" ambiguity observed in other C-like languages. We shouldn't be deliberately adding new syntax in that we know is similarly ambiguous.

Ambiguity isn't just about the compiler producing a single parse tree. It's also about the humans reading the code. Javascript's syntax isn't ambiguous per the computer -- there's a single correct parse which all javascript parsers produce for any valid syntax. However, Javascript's ASI is commonly known to be problematically confusing. You don't need to use semicolons in Javascript because of it... until you do. You can break expressions onto multiple lines... until you can't because of ASI.

Ultimately, the small savings aren't worth the ambiguity and potential confusion for humans.

(It's even more bad if you're suggesting that how code parses should in any way depend on types. This is a complete nonstarter.)

14 Likes

what about ..method()?

the only ambiguous could be, a..b is a std::ops::Range, but Range is not callable, thus NO_MATTER_WHAT..method() won't compile.

That just looks like a typo.

8 Likes

It seems the only solution is write self.method() as method() directly, and raise an warning if we use method function.

if the method method is defined elsewhere, we might specific where it was defined.

examples:

//use mycrate::method; // it could be called by both mycrate::method and the following call.
fn method() -> i32 {
    42
}
struct Foo();
impl Foo {
    fn method(&self) -> i32 {
        0
    }
    fn associated() -> i32 { // associated functions, could also be called by associated() rather than Self::associated
        0
    }
    fn calling(&self) {
        println!("{}", method()); // raise an warning during compilng since this method is ambiguous, print 42 since it is better to call global function as a default behavior
        println!("{}", self.method()); // return 0, no warning is generated
        println!("{}", crate::method()); // return 42, no warning is generated
        println!("{}", associated()); // return 0, but raise an warning during compilng if this method could be ambiguous.
    }
}

Since people seldom wrote different method with same name, this syntax sugar might be sweet.

This is a problem with C++ that I'm glad Rust didn't repeat.

self. serves well to clearly identify method calls, and calls without . refer to global methods. That helps make code modular and composable.

As an example, traits would otherwise introduce further ambiguities here:

fn func() -> i32 {
    0
}

struct S();
impl S {
    fn calling(&self) {
        println!("{}", func()); // This is unambiguous
    }
}

trait Trait {
    fn func(&self) -> i32;
}
impl<T> Trait for T {
    fn func(&self) -> i32 { // This should not make the above ambiguous.
        1
    }
}

Losing that kind of modularity and composability is not worth saving five characters on method calls.

28 Likes

That's the kind of syntax saccharine which leads people in C++ and Java to encode the method vs function distinction via a Polish naming convention: my_field, our_method(). What's the point of removing the syntactic difference between functions and methods, only to make users invent their own syntax for it?

8 Likes

An alternative solution here is to add a hotkey to the editor to add self. and use that, and I'm kinda partial to that as it's really annoying when I read C++ code and can't tell if it's calling the class' method or an external function.

1 Like

I don't know why this would be hard to read. A dot following a block (be it for, if, an ordinary block ...) is interpreted as a dot operator for the result of the block since rust rust blocks results in value this is the done today and I don't want to change. I remember seeing a language with statements similar to rust which uses this approach to refer to self members and methods but I don't remember its name.