In Rust a tuple can be indexed using a dot (e.g.: x.0
), while an array can be indexed with square brackets (e.g.: x[0]
). At first glance this seems to me as if it would make it harder to refactor existing code, without serving any actual purpose. However, I am probably just missing something. Did the creators of Rust ever comment on this and told us why they chose to build the language that way?
This tuple field access syntax was introduced in RFC 184 (discussion thread). Before that, you had to destructure all tuples (and tuple structs) to access their values, or use special traits in the standard library.
The RFC itself does not go into a lot of detail regarding the alternative [index]
syntax, but the discussion thread does. I see three main reasons why we ended up with the .index
syntax:
[]
is related to std::ops::Index
The [index]
syntax is strongly related to the ops::Index
trait. It allows you to overload that operator for your own types. The way the trait is designed, the index
method (which is called when you use []
) has to return that same type every time. So ops::Index
cannot be used for heterogeneous types. And since []
is very related to the trait, it might be strange to have a few special usages of []
that don't use std::ops::Index
.
As also pointed out on reddit, indexing as such (i.e.
tuple[0]
,tuple[1]
etc.) wouldn't make sense as an alternative, because tuples are heterogenous. (They definitely couldn't be made to implement theIndex*
traits.)
— Comment
Indexing syntax [is] actually a really bad fit. Notably, indexing syntax everywhere else has a consistent type, but a tuple is heterogenous so
a[0]
anda[1]
would have different types.
— Comment
Tuples/tuple structs as structs with anonymous fields
Rust has other heterogeneous data types: struct
s. And you access their fields with the .field
syntax. And describing tuple structs as structs with unnamed fields, and describing tuples as unnamed tuple structs, it makes sense to treat both a bit like structs. Then, the .0
just feels like referencing an unnamed field.
I feel, especially in statically-typed languages, the types of a[1], a[2], ..., a[N] should be the same.
Yes, that's why nobody is advocating for adding indexing syntax to tuples. The tuple.1 syntax is a much better fit; it's basically anonymous field access, rather than indexing.
— Comment
Tuples and tuple structs are just structs with anonymous fields ordered by their definition, so both should support syntax for accessing a field as an lvalue directly to make working with them more consistent and easier.
— Comment
Influenced by Swift
Swift already had that syntax and seems to have been an influence:
For reference, Swift allows this tuple indexing syntax, and allows for assignment with it.
— Comment
+1 swift has a lot of nice pragmatic tweaks, and this is one of them, IMO.
— Comment
thanks to swift there's going to be a large community familiar with the .0 .1 ... notation
— Comment
As an aside: as can be seen by this thread, the "designers of Rust" really are, for the most part, just the community members. Sure, the RFC process had its problems back then (and it's still not perfect), but you can read most of that discussion online, with community members commenting on the proposed syntax.
I haven't seen any explicit mention of the reasoning behind the decision but it makes sense considering the original meaning of the indexing syntax. Dating back to C, arrays were contiguous meaning they stored data such that each byte of data began as the previous one left off. When you accessed the Nth element of an array, you were really accessing the data which started N * sizeof(type)
bytes away from the start of the array.
---------------------
| 0 | 1 | 2 | 3 | 4 |
---------------------
This method only works when each value in the array is of the same type and therefore uses the same amount of memory, as is the case with Rust arrays
and slices
but not necessarily so with the tuple
type. Had the designers allowed access to tuple data with indices it may have caused those learning Rust to make incorrect assumptions about the use of tuples.
Tuples are generally used to make simple "data classes" which only store data and do not need any associated methods. So you should consider a tuple a struct where all fields are public and accessed by number.
[]
can be used to access fields of objects. Finally, there are many usages of []
in Rust that don't work like N * sizeof(T)
-- e.g. HashMap
. You can even define the meaning of []
yourself with std::ops::Index
. –
Ephram Short explanation: Tuples and arrays are very different. Tuples aren't really a list at all, they're just a struct with automatically assigned names that happen to be the same as an array. Other than that, they have nothing in common. You wouldn't use square brackets to access a struct and you also wouldn't need to dynamically access the fields of one.
Medium explanation: The posisitions in a tuple matter and can have different types and sizes. Arrays just have a size and a type, the order doesn't matter and you can iterate through. This is demonstrated by how you declare their types. Tuples like this (bool, i32, i64)
and arrays [i32; 5]
. In the example tuple, the different positions all have different sizes and types. Mixing different types only works because you know which position which type is in. You would never ever have to refactor any code to switch between the two because they are for completely different things and just happen to resemble each other.
Long explanation: Tuples are just structs with automatic names and in C dots are used to get fields from structs and square brackets are used to get elements from arrays. Because of the different sizes of tuple fields, the field names have to be known at compile time. So the different syntax makes sense, since anything other than a field name isn't possible. Annoyingly, Python lets you dynamically access tuple fields and even loop thhrough it, which makes no sense and makes this harder to explain.
© 2022 - 2024 — McMap. All rights reserved.