Yet another thing that irks me in rust

If people misunderstand your post, you can work on explaining yourself better, or you can ignore the less useful comments and engage with the useful ones. If you need to re-explain yourself, try do it once and focus on clarity. Do not repeatedly post the same arguments, or feel compelled to individually engage each person who replies. Slow down a bit, and focus on clearly making your technical points.

(This goes for everyone else engaged in these threads, too!)

11 Likes

It sure make sense what your saying. Will try to do that.

3 Likes

You got linked an issue and an RFC, that covers your concerns. Maybe, you should read them before commenting any further, otherwise this thread won't be progressing in any constructive way.

TL;DR: People have made similar suggestions, already. If no one does the work, then it will never make it into Rust. Time is a finite resource and people without contract will allocate it as they see fit.

1 Like

That only confirms that I'm not the only one that see it as something that can be improved.

I think it's all a matter of etiquette, good manners.

Stating that this or that feature is inconvenient, asking why it is and perhaps suggesting improvement is one thing.

Barging in and shouting "half baked" is another.

As it happens all programming languages are "half baked" even after decades. C, C++, Python, Javascrip, etc, etc. That is why new versions of the language come every now and then.

Rust is certainly still evolving.

5 Likes

Sure, that is true, but it shouldn't be an excuse in form: if others are broken it is OK for us to be too.

As for manners and etiquette? Sure, I agree with that, but let me explain why I feel like that about rust and why I'm getting so frustrated.
Rust is being "advertised" as the new, wonderful, problem solving, C++ replacement, that people who care about memory corruptions should start using it etc.
But unfortunately to me, what I see is that it is nothing more but "advertisement". Rust promises lots of things but the reality is that this is not mature language yet to be of any significance.
So why do I bother/get irritated?
My answer to that is, that I feel that I'm being cheated. I was promised something that I bought (ie I've spend hundredths of hours of my own private time) and yet that thing I bought doesn't deliver what was promised to me by the salesman.
No mature libraries, half baked features, nothing, just language with bunch of enthusiasts.
Maybe rust one day will be fantastic language, but it will happen in 10-15 years. But the salesmen pitch is that it is already at that stage and this is misleading and dishonest.
That's all.

Personally, I don't see where Rust ever does "get rid" of the -> syntax implicitly.

If I understand correctly, this syntax originally means "given a pointer to struct, dereference it and get field/invoke method on this struct". However, in Rust this is possible only in one case: if the method receiver is Copy. In most other cases, we have quite the opposite: "given the owned value, make a reference to it and invoke method on this reference".

1 Like

I don't know what to say. As a long time user of Pascal, C, C++, Python, Javascript and a bunch of other languages over the decades and having spent a lot of time this last year getting into Rust and actually building code that is now in production, I find Rust more than delivers on it's promises. Rust and it's libraries/ecosystem/community has delivered more that I could have hoped for.

No way am I going back to the crudity of C/C++ unless some terrible catastrophe happens. Other languages cannot even do what I often need to do.

1 Like

It probably won't help, but let's please stop arguing about tone and blindly speculating. This stuff is all public knowledge if you wish to look.

It was argued pretty extensively back in the pre-RFC days. If you want help finding something, the right incantation for DuckDuckGo is:

site:mail.mozilla.org autoref rust

Of, if you think it might be in the post-RFC days, use instead the search function on the rfc repo.

Some good sources of info that I found:

[rust-dev] Removing some autoref magic:

And http://smallcultfollowing.com/babysteps/blog/2013/11/20/parameter-coercion-in-rust/ is probably worth reading, also.

Also, here's an RFC that touches on other aspects of auto(de)ref, where &something can be turned into &*something automatically, and what they were hoping to preserve by not being able to insert dereferences literally anywhere.

p.s. When documents that old talk about ~T, they are talking about the type that we now call Box<T>.

edit2: there's a broken link in the mailing list thread to the 2013 meeting minutes. Those minutes were moved to https://github.com/rust-lang/meeting-minutes/blob/master/weekly-meetings/2013-11-19.md#autoderef

6 Likes

[Moderator note: I hid a couple of replies that were derailing into other topics. Let's keep this thread going only if there is more to say specifically about auto-ref and auto-deref in Rust expressions.]

The difference between ./-> and * is that using . is unambiguous - pointers don't have fields so dereferencing before a . is always correct. OTOH * can be ambiguous, since you can perform operations on both Box and i32.

4 Likes

how about .clone() on an Arc? seems kinda ambiguous to me.

2 Likes

Why would getting rid of * make the language more clear or more clean?

. is for member access. * is for dereferencing. When you're doing member access, you often need to dereference first, so . does a little bit of magic to make that more ergonomic. It does not mean that every use of * can be eliminated (which seems to be what you're talking about).

When you're doing other things, you often do not need to dereference first. Add<u64> is implemented for both u64 and &u64, and while in the case of &1 + 2 it doesn't really matter which is picked, it does matter that the compiler can pick one unambiguously and in a way that will not confuse people. Sure, it would be possible to come up with some half-baked rules for when dereferencing happens automatically, but then we'd be stuck with them forever. Another weird language quirk for students to be baffled by 20 years down the line, like how C++ has a dozen rules for when the language automatically supplies different member functions if you don't write them.

I personally don't want to have to remember a dozen rules for when the language automatically dereferences things. Deref is already pretty magical, to be honest, but the magic is contained to member access with . (autoderef) and coercion sites (deref coercion). I'm not excited about adding another place where Deref is magical, with its own set of rules that isn't exactly the same as either of the other two. People already get confused between autoderef and deref coercion even though they're basically completely separate mechanisms.

When you do get tripped up, the solution is usually obvious, like in your assert_eq example: you have an i32 and a pointer-to-i32, so you dereference the pointer to get an i32 and compare them. No ad-hoc rules, very little magic, just simple-minded, self-evident, clean code. If Rust were trying to dereference stuff to make things easier for you, and it happened to dereference something you didn't mean to dereference, that would lead to confusion and messy code.

That's my opinion. You may feel free to disagree, think that removing * (even in a subset of cases where it's currently needed) would make the language cleaner. I've given my argument for why I think it would have the opposite effect. But we're both talking about abstracts. If you think there's a way to implement auto-deref for operators that aren't ., and it wouldn't make the language unnecessarily more complex and harder to understand, please share. I'm open to changing my mind.

3 Likes

With operators like + that can potentially move their operands, I think there are reasonable arguments for requiring explicit deref.

However, I think it would be very reasonable for the comparison operators like == and >, which always borrow their operands, and already have auto-ref behavior, to also perform auto-deref. I can't think of any significant clarity penalties from this. In fact, I think it would make some code less confusing.

For example, comparing Box<T> to &T currently requires code like this:

fn foo<T: PartialEq>(x: Box<T>, y: &T) {
    if *x == *y {
        // ...
    }
}

This often trips up new Rust programmers, because it looks like the *x should move out of the Box. (It doesn't, because the existing auto-ref behavior of the == operator makes this equivalent to PartialEq::eq(&*x, &*y).)

Adding auto-deref would allow us to write this as simply x == y. This would also let us replace this common pattern:

vec.iter().find(|x| **x > 0)

with this:

vec.iter().find(|x| x > 0)

Note that this only needs to allow the same set of type coercions as the existing "deref coercion" feature, which is not limited to the . operator.

6 Likes

It's easy to see why writing Rust doesn't feel natural to you. (edit: this was in response to a now-deleted post in which OP said they found Rust to not feel natural to write) First of all Rust isn't OO and doesn't support many OO features. It takes a while to switch paradigm mindset, and is very frustrating, especially if you haven't even used a non OO language before.

Also, Rust is very strict. This is a legitimate non-paradigmal flaw in Rust. However, again, the only solution is practice until you fully understand how rustc works.

Rust is by design a frustrating to learn language. This unfortunately means that the barrier to discussion on it is very high and you do need to give it a chance before you critique it. This is not to say that Rust can't be less intimidating for beginners, it certainly can and should be, but insulting the language will do no good.

1 Like

Isn’t getting rid of -> as easy and trivial as forcing the user to write (*x).foo() instead of x->foo()? Of course, this syntax is terrible, but Rust has a sophisticated method and field resolution that makes x.foo() work instead of (*x).foo() in most cases. Since explicitly writing (*x).foo() is rarely needed anymore, it’s reasonable to abandon the -> sugar entirely.

However, the argument along the lines of “if we don’t need ->, why don’t we also get rid of * is IMO invalid, as it seems to assume that every x->foo() can be written as x.foo(). The example of x.clone() vs (*x).clone() for an x: Arc<T> shows how the explicit version can still be needed for disambiguation. In a sense it is true then, that the fact that * still exists is what allows for the “removal” of -> in the first place.

Ambiguities like clone() being available on both x and *x are the cases where . can’t fully replace a hypothetical ->. And “ambiguity” is a term that describes some of the problems around getting rid of *, too. Reading through the linked RFC for example, a drawback seems to be that the type-checker gets less information (no propagation of type information from the LHS to the RHS anymore), and less type information can mean more ambiguity.

After all, deciding where and how values can be implicitly converted, by e.g. dereferencing, is a super hard design question. There is no straightforward perfect solution to this problem, and I’m sure the designers of the current state of Rust’s approach to implicit conversions do hate the quirks, trade-offs, and limits that it has as much as you do; I’m rather amazed how smooth things currently already work in Rust, in most cases, and without introducing dangerous foot-guns—and I’m positive that in particular the situation around the unfortunate need for * for operations like < and == will be resolved, too, eventually.

6 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.