# Restricting "place expressions" (aka lvalues)?

**URL:** <https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757>\
**Category:** language design\
**Created:** [June 20, 2018, 3:19am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757 "2018-06-20T03:19:04Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Wyverald](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/wyverald/32/2060_2.png) [@Wyverald](https://internals.rust-lang.org/u/Wyverald)\
**Post date:** [June 20, 2018, 3:19am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/1 "2018-06-20T03:19:04Z")

</div>

This is not urgent at all, but just a nit I found the other day.

According to the [Rust reference](https://doc.rust-lang.org/reference/expressions.html#place-expressions-and-value-expressions), all array indexing and field access expressions are place expressions. This means that the following is valid rust code:

```rust
(1, 2, 3).1 = 4;
MyStruct { field: value1 }.field = value2;
vec![1, 2, 3][1] = 5;

```

This might not seem alarming at all, but if you switch the temporary values with function return values, things can become more confusing:

```rust
function_that_returns_struct().field = value;

```

It’s a no-opt, but the user who wrote this code might have expected some reference to be returned.

C++ solves this problem by saying that field access is only an lvalue when the struct is an lvalue.

Admittedly this is already a problem with method calls, as `function_that_returns_struct().set_field(value);` is similarly a no-op, but that’s IMO a deeper problem with the value const-ness model in languages like Rust and C++. I have to think more on this.

---

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [June 20, 2018, 3:38am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/2 "2018-06-20T03:38:34Z")

</div>

I definitely think this is a good idea. In fact, aggressively linting against all of the examples you provide, which are generally errors in C++ iirc, is probably the right call. Such things should certainly be _allowed_, but they should be discouraged because they can and will be optimized away.

In the last example you give, maybe we want to restrict taking references of value expressions? I’m not sure how to handle it, really, since I like being able to write things like `&0` for “push `0` onto the stack and give me its reference”.

---

<div class="post-metadata">

**Author:** ![dtolnay](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dtolnay/32/1447_2.png) [@dtolnay](https://internals.rust-lang.org/u/dtolnay)\
**Post date:** [June 20, 2018, 3:53am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/3 "2018-06-20T03:53:57Z")

</div>

The variant of this that I have seen many people confused by is that you can reassign fields of `const` structs. [playground](https://play.rust-lang.org/?gist=24cc963296e339c4bf8e1b8e039c5c13)

```rust
#[derive(Debug)]
struct MyStruct {
    field: i32,
}

const C: MyStruct = MyStruct { field: 0 };

fn main() {
    C.field = 1; // ???
    println!("{:?}", C);
}

```

---

<div class="post-metadata">

**Author:** ![Wyverald](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/wyverald/32/2060_2.png) [@Wyverald](https://internals.rust-lang.org/u/Wyverald)\
**Post date:** [June 20, 2018, 3:57am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/4 "2018-06-20T03:57:03Z")

</div>

Huh. This seems to be a different problem, but arguably even more confusing. What’s actually happening here? (In C++ terms, “C” would be an lvalue indeed, but it would be const so no assignment can happen.)

---

<div class="post-metadata">

**Author:** ![dtolnay](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dtolnay/32/1447_2.png) [@dtolnay](https://internals.rust-lang.org/u/dtolnay)\
**Post date:** [June 20, 2018, 4:05am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/5 "2018-06-20T04:05:51Z")

</div>

I believe it is exactly the problem you described. My snippet is equivalent to:

```rust
fn main() {
    MyStruct { field: 0 }.field = 1;
    println!("{:?}", MyStruct { field: 0 });
}

```

---

<div class="post-metadata">

**Author:** ![Wyverald](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/wyverald/32/2060_2.png) [@Wyverald](https://internals.rust-lang.org/u/Wyverald)\
**Post date:** [June 20, 2018, 4:08am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/6 "2018-06-20T04:08:26Z")

</div>

Ah, I wasn’t aware that’s what `const` meant in Rust. It is the same problem then.

I think I just remembered that `static` would be the equivalent of what I thought it was (ie. fixed location in memory, immutable, hence non-assignable).

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [June 20, 2018, 10:19am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/7 "2018-06-20T10:19:35Z")

</div>

I’d much rather this were a warn-by-default lint in the specific case of dead assignments (that would be a good idea in general, in fact!) than a hard error. The problem with temporaries not being lvalues is that it disables some absolutely valid and often-used constructs, such as:

`hashmap.get(&fn_returning_a_struct().field)`

---

<div class="post-metadata">

**Author:** ![Wyverald](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/wyverald/32/2060_2.png) [@Wyverald](https://internals.rust-lang.org/u/Wyverald)\
**Post date:** [June 20, 2018, 12:34pm UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/8 "2018-06-20T12:34:35Z")

</div>

You can already take the reference of an rvalue in Rust though. That is, `hashmap.get(&fn_returning_a_struct())` already also works.

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [June 20, 2018, 3:04pm UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/9 "2018-06-20T15:04:02Z")

</div>

I know that, otherwise `&fn_returning_a_struct().field` couldn’t possibly work either – it just seems we have different definitions of what an `lvalue` is. My point is, regardless of what we call these kind of values, special-casing and forbidding assignment as opposed to taking the address seems inconsistent and surprising.

---

<div class="post-metadata">

**Author:** ![Wyverald](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/wyverald/32/2060_2.png) [@Wyverald](https://internals.rust-lang.org/u/Wyverald)\
**Post date:** [June 20, 2018, 10:14pm UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/10 "2018-06-20T22:14:29Z")

</div>

> [@H2CO3](#):
>
> I know that, otherwise `&fn_returning_a_struct().field` couldn’t possibly work either

That's not true. In Rust `func().field` is a place expression (lvalue), _even though_ `func()` is a value expression (rvalue). That's the point of this thread. The fact that you can take the reference of an rvalue is orthogonal to this topic.

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [June 20, 2018, 10:51pm UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/11 "2018-06-20T22:51:13Z")

</div>

Okay, again, I’m not trying to litigate the name or definition of either of these constructs – my point is merely that arbitrarily restricting assignments (that is, emitting hard errors) only in special cases based on rules that differ from those governing address-of seems like an ugly afterthought.

---

<div class="post-metadata">

**Author:** ![Wyverald](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/wyverald/32/2060_2.png) [@Wyverald](https://internals.rust-lang.org/u/Wyverald)\
**Post date:** [June 20, 2018, 11:41pm UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/12 "2018-06-20T23:41:39Z")

</div>

I don’t necessarily agree with that, either. In Rust the “&” operator applies to any kind of expression, regardless of whether it can be assigned to, and people like that (see drXor’s post above, where he raises the example of `&0` being valid and useful Rust).

On the other hand, things appearing on the left side of an assignment _has_ to be a "place expression’ (lvalue). That’s hardly arbitrary – you would supposedly never want to write `23 = 7;` in Rust (and this indeed doesn’t compile today).

---

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [June 21, 2018, 4:24am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/13 "2018-06-21T04:24:01Z")

</div>

I still think that such statements should emit dead code lints, since that's what they are; such code should be optimized out even in debug mode, since it can't be observed.

> [@Wyverald](#):
>
> That’s hardly arbitrary – you would supposedly never want to write `23 = 7;` in Rust (and this indeed doesn’t compile today).

Well, that's different. `23` is not a place expression, while `(23,).0` is. Field access is always a place, with the desugaring

```rust
expr.field = foo;
// ...
let $temp = expr;
$temp.field = foo;

```

if `expr` isn't a place.

---

<div class="post-metadata">

**Author:** ![Wyverald](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/wyverald/32/2060_2.png) [@Wyverald](https://internals.rust-lang.org/u/Wyverald)\
**Post date:** [June 21, 2018, 5:40am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/14 "2018-06-21T05:40:51Z")

</div>

Exactly my point – why does `(23,).0 = 7;` compile when `23 = 7;` doesn’t?

By the same logic, you could do the same desugaring for non-field-access: `expr = foo;` could technically be desugared into `let $temp = expr; $temp = foo;` when `expr` is not a place. That would be very useless – so why do we tolerate the field access case?

---

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [June 21, 2018, 5:54am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/15 "2018-06-21T05:54:14Z")

</div>

In C++, `expr.field` is an lvalue iff `expr` is an lvalue. This is not the case in Rust; `expr.field` is _always_ a place (i.e., an lvalue). You can always assign to places. Meanwhile, you can take the reference of anything, which, when we take the reference of a value, produces a reference to the stack. We can dereference the pointer to obtain a place. This is why the following is valid Rust, and _should_ be valid Rust:

```rust
*&mut 23 = 7;

```

This is a nop, because it only fusses around on the stack, unobservable from the outside. I also think it should be linted against, because it’s dead code.

On the other hand, you suggest that assigning to values should be allowed. On one hand, I am ok with this, because we’d just spit out a dead code warning and move on with our lives. But then you’re allowed to write bizarre things like

```rust
const C: u8 = 0;
C = 1;

```

which I think should be a hard error, like in C++, for our collective sanity. As much as I think `const` items are really just a typed `#define`, I think we should stick to some degree of common-sense usage.

I think I’d like to hear other internals folks weigh in on this, since I think a lot of this comes down to a matter of taste, and what is a lint versus a hard error.

---

<div class="post-metadata">

**Author:** ![Wyverald](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/wyverald/32/2060_2.png) [@Wyverald](https://internals.rust-lang.org/u/Wyverald)\
**Post date:** [June 21, 2018, 5:58am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/16 "2018-06-21T05:58:43Z")

</div>

> [@mcy](#):
>
> On the other hand, you suggest that assigning to values should be allowed.

That's not what I'm trying to suggest; I'm saying the current situation is inconsistent, and the way to fix it is to make `expr.field` _not_ a place if `expr` is not a place (ie. do what C++ does).

I just read my first post again and realized that it might not have been clear _that_ was what I was suggesting -- sorry about that.

---

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [June 21, 2018, 6:01am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/17 "2018-06-21T06:01:13Z")

</div>

Ah, I see! Well, I’m curious if this change would break anything… after all, C++ has several arcane rules surrounding the relationship between rvalues and lvalues (and nonsense like `T&&` and `std::forward` that Rust should avoid at all costs). I’ll need to think it over a bit.

---

<div class="post-metadata">

**Author:** ![Wyverald](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/wyverald/32/2060_2.png) [@Wyverald](https://internals.rust-lang.org/u/Wyverald)\
**Post date:** [June 21, 2018, 6:08am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/18 "2018-06-21T06:08:09Z")

</div>

Just a tiny note wrt `T&&` and `std::forward` in C++. Those are about lvalue and rvalue _references_, which are different (but related) concepts from lvalue-and-rvalue-ness (value categories). All Rust references are technically lvalue references so there’s no need to worry about that nonsense making it into Rust 🙂

(footnote – C++ rvalue references simply correspond to passing by value in Rust – which is _so_ much more elegant)

---

<div class="post-metadata">

**Author:** ![gbutler](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gbutler/32/3670_2.png) [@gbutler](https://internals.rust-lang.org/u/gbutler)\
**Post date:** [June 21, 2018, 7:24am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/19 "2018-06-21T07:24:00Z")

</div>

> [@mcy](#):
>
> such code should be optimized out even in debug mode, since it can’t be observed.

EDIT: Never mind. Of course, the call to the func wouldn't be optimized away, just the setting of the field of the returned struct which would not have any possible side-effect.

~~What about side-effects?~~

```rust
some_func_that_launches_missile_and_returns_launch_report_struct().field = "Yay!";

```

---

<div class="post-metadata">

**Author:** ![crlf0710](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/crlf0710/32/1987_2.png) [@crlf0710](https://internals.rust-lang.org/u/crlf0710)\
**Post date:** [June 21, 2018, 8:00am UTC](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757/20 "2018-06-21T08:00:59Z")

</div>

> [@Wyverald](#):
>
> It’s a no-opt

It is _not necessarily_ a no-op, since it may mutate the effect of `Drop::drop`....

[Next page](https://internals.rust-lang.org/t/restricting-place-expressions-aka-lvalues/7757.md?page=2)
