Did the Designers of Rust ever publicly say why the syntax of indexing an array is different to indexing a tuple?
Asked Answered
C

3

9

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?

Convict answered 1/8, 2021 at 16:24 Comment(3)
Rust is a strongly typed language. Indexing a slice/array will always yield the same type because only one type can be in the entire slice. "Indexing" a tuple actually means "accessing a field", which can result in different types based on which anonymous field you access. The confusion may arise because languages like Python allow indexing and even iterating over tuples, which can easily be done in a duck-typed language. Voted to close because I edited out the opinion-based approach to the question, which you reversed, so the question still asks for an opinion instead of a technical reasonPettaway
@AKX That might be the reason, however it feels to me that the similarities to arrays are bigger. The book even introduces both right next to each other, see: doc.rust-lang.org/book/…Convict
@Pettaway Good point regarding the indexing. Regarding the edit: Your edit made it opinion based, while my original question wasn't. If you ask "Why...?" then that's a question about an opinion. If you ask "Did X say why...?" then that's a question about facts. At least in principle one can find out if X ever commented on something or not. There is no opinion. If I am wrong about that and this was maybe already discussed on meta than I would appreciate the link to it.Convict
E
12

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 the Index* 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] and a[1] would have different types.

Comment

Tuples/tuple structs as structs with anonymous fields

Rust has other heterogeneous data types: structs. 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.

Ephram answered 2/8, 2021 at 8:50 Comment(0)
E
2

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.

Estragon answered 1/8, 2021 at 16:48 Comment(2)
To be honest, I don't think this is a particularly strong argument. There are plenty of languages with heterogeneous arrays/tuples that can be index by square brackets. Not all Rust programmers come from C. Also, take JS, where [] 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
@LukasKalbertodt I agree, you answer is definitely better informed than mine. This is mostly just how it made sense to me especially among statically typed languages., and to try and help the OP at least come to terms with a piece of syntax that confused them. The RFC you referenced is greatly appreciated!Estragon
T
0

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.

Tress answered 19/1 at 14:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.