Allow mut value, not just mut reference

For flexibility as I said, people would have the choice of the spot for mut place. Actually mut is allowed in expression but in the form of &mut. My proposal would be parallel to that.

&mut in an expression is quite different from mut in a pattern. &mut creates a exclusive reference, and mut allows a binding to be changed. They’re only analogous in the sense that they allow changes to happen, but it doesn’t go much farther than that. They allow totally different kinds of changes.

1 Like

Basically adding syntax to the language itself is expense, not income, though it often(usually?) can be justified by being more usable/idiomatic/expresive/etc. So “more flexible” can’t be a reason to add a syntax to the language. At least, it’s not an enough basis to persuade people in this forum.

5 Likes

I’m just proposing to allow place flexibility of mut.

...okay. To be honest, at this point I'm still not even sure what you're suggesting. On the one hand you're saying that you're "just allowing mut to appear in more places." On the other hand, it looks like you're trying to introduce some concept of a "mut Type" or something. (at least, that would seem to be the level of complexity needed just to make even the simplest edge cases work)

There's no point in arguing when I don't even understand what I'm arguing against. So please help us understand your proposal. Here's a bunch of simple yes/no questions. Please answer them according to how your proposal is intended to work.

// 1a. Is this valid syntax?
// 1b. If so, is y mutable?
let (x, y) = (3, mut 3);

// 2. Does this compile?
let tuple = (3, mut 3);

// 3a. Does this compile?
// 3b. If so, are x and y mutable?
let (x, y) = mut (3, 3);

// 4a. Is this valid syntax?
// 4b. Is y mutable?
let f = |x| x;
let y = f(mut 2);

// 5a. Is this valid syntax?
// 5b. If so, is y mutable?
let f = |x| mut x;
let y = f(2);

// 6. Is y mutable?
let x = mut 3;
let y = x;

Edit: removed question 7.

2 Likes

With this and the several other similar threads you’ve opened: What problem are you trying to solve?

Rust, as a language, is very amenable to proposals for potential language improvements. However, someone making such a proposal needs to explain the problem they’re trying to solve, and why the existing language doesn’t solve the problem (or doesn’t do so effectively enough).

In doing so, such a proposal should also demonstrate a clear understanding of how the language already works, at least in the area in question, in order to motivate a change to how it works.

Many syntax proposals, for instance, get pushback of the form “you could do this with a macro”, and it’s up to the proposer to explain why that doesn’t suffice.

In the case of this and several other proposals you’ve made recently, there seems to be an underlying theme of some kind of frustration you’re having, perhaps between the Rust language and the way you’re trying to use it, along with (intentional) differences in behavior between Rust and other programming languages. That’s fairly normal, especially when learning the language. However, rather than making an array of proposals for how Rust could behave differently, you might consider seeking some help with the difficulties you’re encountering with Rust and with potential solutions or mental models that might make your path easier. For instance, you might try Rust’s friendly IRC channels.

17 Likes

I don’t have any problems, I am just suggesting proposals that could lead rust better and easier as well as being more wide spread in programming world. I would like people to use rust for example instead of go. That’s what I’m trying to achieve.

Not "somebody".

@medozs I'm not sure why you expected people to like this proposal when they didn't like var. But, really, it has the same problem; it doesn't fit with the way Rust's pattern-matching system works, and it doesn't seem to improve things. It doesn't even make the code shorter! At least var had that going for it.

Do you have any evidence that micro-scale syntax flexibility actually does make programming languages more successful? Because I think the widespread success of Python is evidence that it's not required.

1 Like

No, there aren’t any new types. It’s just equivalent to let mut a = value but with mut is moved to expression. The behavior stays the same.

// 1a. Is this valid syntax? // 1b. If so, is y mutable? let (x, y) = (3, mut 3);

a. Yes, b. Yes

// 2. Does this compile? let tuple = (3, mut 3);

A. Yes

// 3a. Does this compile? // 3b. If so, are x and y mutable? let (x, y) = mut (3, 3);

a. Yes, b. Yes

// 4a. Is this valid syntax? // 4b. Is y mutable? let f = |x| x; let y = f(mut 2);

a. No, b. No

// 5a. Is this valid syntax? // 5b. If so, is y mutable? let f = |x| mut x; let y = f(2);

a. No, b. No

// 6. Is y mutable? let x = mut 3; let y = x;

A. No

Me and others here in this forum are just making suggestions and ideas, it’s up to you guys concerned to evaluate and discuss such proposals.

(As a quick aside: the use of & in patterns is in fact the right choice, because patterns aren’t operators, but they are constructs that mimic the structure / type of the values they are matching. Therefore, if reference types are spelled &T, and references are obtained using &value, then reference patterns should also be spelled &inner_pattern. It’s similar in principle to how you write Some(inner_value) in a pattern, instead of using is_some() and unwrap(), hopefully.

Patterns destructure values (as opposed to composing them), so if you are thinking in terms of operators, it might often seem they work “backwards”. However, that’s just a result of their nature: they are more or less duals, in a Haskellian sense, to value and type constructors such as enum variants and references.)

3 Likes

To be more specific, this means the syntax “should have been” this:

let a = &Some(1);
match a {
    &Some(*x) => {}
}

That is, exactly as it is today, with &-in-expressions and &-in-patterns having opposite meanings, but with ref replaced with *.

(I suspect one reason we didn’t get that is that it would lead to things like *mut x, which looks an awful lot like a raw pointer type. It’s also just kind of confusing because neither & nor * are quite value constructors the way Some is.)

neither & nor * are quite value constructors the way Some is.

That's true, however type-level & is a type constructor, a function from a type to a type. For all types T, there is a corresponding reference type &T. This doesn't work with dereferencing, though: there's no type-level *. Don't be confused by the same syntax of type-level & and value-level &, they are very different beasts! I was talking about the type-level constructs only.

Incidentally, this is why I don't think * in patterns would have been a good idea. It doesn't match the structure of an expression, because it simply can't — given the lack of a type-level * operation.

Not quite the same. If you had 'destructuring' *, the meaning of mut would have to be different, since * would be a structural element rather than a binding modifier. Right now, if you wanted a mutable-binding-of-reference-to-the-slot, you would write mut ref x, which makes the binding ref x mutable. However, if you wrote mut *x, the mut would no longer be applying to the binding, it would apply to the slot corresponding to *x, making mut *x equivalent to ref mut x. To get a mutable binding, mut would have to be applied directly to the binding, like *mut x. With a ref binding modifier, the meaning of incantations involving both ref and mut are essentially arbitrary, and I guess it made sense not to force people to read them inside out the way you do with structural pattern elements. After all, you're constructing a binding rather than deconstructing a value at that stage.

Just because I didn't quite take your meaning immediately, the type level operation corresponding to Some(_) is Option<_>. At first I thought you meant that the type-level operation has to have the same syntax as the value operation, which of course doesn't work in general. That it happens to line up for & is the exception rather than the rule, which is what threw me off when I first read your comment.

Of course, you could create a type-level operation:

type Dereffed<T> = <T as Deref>::Target;

But yeah, if we limit ourselves to canonical types rather than arbitrary aliases, then it's very clear that & adds structure while * removes it. If you think of patterns as destructuring, then reversing * is undestructuring. Thus, it doesn't fit with everything else (though it does have a clear meaning in the more general logic of matching: matching *x to y makes x = &y).

Yeah, sorry, I should probably have been more clear about that. (Also, re-reading my own comment, I see I did mention value-level &, I shouldn’t have brought it up probably.) Yes, there certainly is a correspondence between * and destructuring, as you wrote.

It’s not so much what you wrote, it’s that my brain took a wrong turn while reading it, so I figured I’d share my experience with the class :wink:

1 Like

Comparison between &mut and mut

&mut placement:

  • Can come as let pattern: let ref mut a = ..
  • Can come as type pattern: ..:&mut ..
  • Can come as value expression: ..=&mut ..

mut placement:

  • Can come as let pattern: let mut a = ..
  • Can come as type pattern: ?
  • Can come as value expression: ?

ref mut in patterns is not the same a &mut. In fact, they’re opposites.

3 Likes

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