Rust and Swift (vi)
Collection types and the difference between syntax and semantics.
I am reading through the Swift book, and comparing it to Rust, which I have also been learning over the past month. As with the other posts in this series, these are off-the-cuff impressions, which may be inaccurate in various ways. Iâd be happy to hear feedback! Note, too, that my preferences are just that: preferences. Your tastes may differ from mine. (See all parts in the series.)
It kind of feels like this summarizes a lot of things about the overall design of Swift:
Although the two forms are functionally identical, the shorthand form is preferred and is used throughout this guide when referring to the type of an array. âThe Swift Programming Language (Swift 2 Prerelease)
The documentation for the various types in Rustâs std::collections
module is hilarious and great. Highly recommended.
One thing that jumped out at me reading this chapter of the Swift book (though I donât think itâs been explicitly discussed yet): Rust doesnât have named parameters; Swift does. There are good reasons for that in both cases, but I suspect this is one of the small details Iâll miss the most in Rust. Iâve been spoiled by Python.
Swiftâs Array
type is analogous to Rustâs Vec
type (usually created with the vec!
macro), not its Array
type. Rust Vec
s and Swift Array
s are dynamically sized and created on the heap, whereas Rustâs Array
s are statically sized and created on the stack. Syntax for creating Array
s in both languages is quite similar (though the results are different):
- Swift:
- Fixed size:
let an_array: [Int] = [1, 2, 3]
- Variable size:
var an_array = [1, 2, 3]
- Fixed size:
- Rust:
- Array:
let an_array: [i32, 3] = [1, 2, 3];
- Vector:
let a_vector: Vec<i32> = vec![1, 2, 3];
- Array:
Thatâs the long form, of course; both languages have type inference, so youâd rarely write it like that. The usual form would be with the type in all of those cases:
- Swift:
- Fixed size:
let an_array = [1, 2, 3]
- Variable size:
var an_array = [1, 2, 3]
- Fixed size:
- Rust:
- Array:
let an_array = [1, 2, 3];
- Vector:
let a_vector = vec![1, 2, 3];
- Array:
Rust also adds the concept of âslices,â which provide access to segments of arrays, and are heap-allocated as pointers to a given item in the array and a length (number of elements) included.
Array
operations in Swift are all pretty reasonable, and surprisingly descriptive. They remind me in a good way of Pythonâs list
methods.
There are a lot of ways to interact with Vec
s in Rust. (Thatâs not a bad thing.) A bit surprising to me was the absence of an enumerate
method, on Vec
itself, but then I discovered that it exists in the IntoIter
struct in the same module, which fully implements the Iterator
trait
. As a result, it has an enumerate
function returning an Enumerate
struct
instance. (Under the covers, I suspect Swift Array
s just implement an Iterable
protocol
, which is similar to this approach in some ways.)
This makes a point Iâm coming back to fairly often: Rust doesnât necessarily put everything on a single object definition, but rather into a set of related struct
or enum
types and trait
s. This is really powerful, but itâs going to take some mental adjustment. In this way, Swiftâs structure and semantics are much more like the languages Iâm used to than Rustâs are (but even there, the use of protocols
gives it considerable new flexibility).
Note that I said semantics, not syntax. Swift and Rust are a great example of how very similar syntax can mask differences in semantics. (For another such example, compare JavaScriptâs syntax and semantics to Javaâs: theyâre superficially similar syntactically, and light years apart semantically.)
Swiftâs Set
type and Rustâs roughly analogous HashSet
both have a contains
method which behaves much like Pythonâs in
keyword. Indeed, and perhaps unsurprisingly, the two types implement many of the same methods in general. This is perhaps to be expected given that the language around sets (as a mathematical concept being mapped down into a representation in a program) is quite standardized.
Because of their stricter typing systems, both Rust and Swift require you to specify the types used in their mapping constructs (Rust has HashMap
and Swift has Dictionary
), though of course both can infer this as well in certain cases. At the most basic level, you canât use arbitrary (hashable) types as keys in mixed fashion like you can in e.g. Pythonâs dict
type, but in practice this shouldnât matter, for two reasons:
- Itâs generally inadvisable to use different types for keys in the same dictionary anyway. To me, at least, that usually indicates the need to step back and think more carefully about the types and data structures Iâm using.
- For the occasional case where it is appropriate, I wonder if you could declare the type as generic in either Rust or Swift. Iâm putting this down as a TODO item for myself to find out!
I really wish that Swift used the Python-style curly-brace delimited syntax ({'key': 'value'}
) for its dictionary literal initializers. I can see, from a syntax reason, why it doesnât: that would overload the block syntax (which Python can avoid because itâs white-space delimited). But itâs really convenient.
Along similar lines, I can see why the Swift designers chose to make all iterables have literal initializers using braces ([...]
); it makes parsing fairly straightforward. However, the result is that itâs pretty difficult to see at first glance what youâre dealing with. It could quite easily be an Array
, a Set
, or a Dictionary
.
This highlights a too-little-appreciated aspect of programming language design: readability. However much we programmers enjoy writing code, the reality is that we will all spend a great deal of our timeâprobably even a majority of itâreading it instead. Thus, while we should care about conveniences for writing code, and being overly verbose can be a pain, we should also concern ourselves with the ease of comprehending code when it is read, and the syntax and conventions a language embraces are a big part of this.
The Dictionary
type in Swift is a pretty close analog to Pythonâs dict
, right down to several of the method names. the same is true of Rustâs HashMap
. Thatâs not a bad thing by any stretch of the imagination.