Skip to content
📖 Auto vs RustBook contents

Common Collections ​

Auto's standard library includes a number of very useful data structures called collections. Most other data types represent one specific value, but collections can contain multiple values. Unlike the built-in array and tuple types, the data that these collections point to is stored on the heap, which means the amount of data does not need to be known at compile time and can grow or shrink as the program runs.

In this chapter, we'll discuss three collections that are used very often:

  • A list (List<T>) allows you to store a variable number of values next to each other.
  • A string (String) is a collection of characters. We've mentioned the String type previously, but in this chapter, we'll talk about it in depth.
  • A hash map (Map<K, V>) allows you to associate a value with a specific key.

We'll discuss how to create and update lists, strings, and hash maps, as well as what makes each special.

Storing Lists of Values with List<T> ​

The first collection type we'll look at is List<T>, also known as a list (equivalent to Rust's Vec<T>). Lists allow you to store more than one value in a single data structure that puts all the values next to each other in memory. Lists can only store values of the same type.

Creating a New List ​

To create a new, empty list, we call List.new():

Creating a new list and adding elements with `push`

Note that we use var to make the list mutable so we can add elements with push. The type of elements is inferred from the first push call — since we push 10 (an int), the list is List<int>.

Reading Elements of Lists ​

There are two ways to reference a value stored in a list: via indexing or by using the ?? (null coalescing) operator for safe access.

Reading list elements with indexing and the `??` operator

In Auto, list[i] returns ?T (Option) — it's Some(value) if the index is valid, or None if out of bounds. The ?? operator provides a default value, replacing Rust's verbose .get(i).copied().unwrap_or(default) pattern.

This is a key safety improvement over Rust: Auto's list indexing never panics. You must explicitly handle the case where the index might be out of bounds.

Iterating Over Lists ​

To access each element in a list, we iterate through all the elements:

Iterating over a list with a `for` loop

Using an Enum to Store Multiple Types ​

Lists can only store values of the same type. When you need to store different types, define an enum whose variants hold the different value types:

Defining an enum to store values of different types in a list

Storing UTF-8 Encoded Text with Strings ​

We've used String extensively in previous chapters. A String is a growable, mutable, owned, UTF-8 encoded string type. Let's look at some common operations.

Creating and Updating Strings ​

Creating and updating strings

Key differences:

FeatureAutoRust
From literal"hello".to_string()"hello".to_string()
From literalString.from("world")String::from("world")
Append strings.push_str("bar")s.push_str("bar")

Concatenation with f-strings ​

Instead of Rust's + operator or format! macro, Auto uses f-string interpolation for string concatenation:

String concatenation with f-strings

F-strings are Auto's preferred way to build strings. They're more readable than + concatenation and don't take ownership of any variables (unlike format! in Rust which borrows).

Indexing into Strings ​

Like Rust, Auto doesn't support direct byte indexing into strings (e.g., "hello"[0]). This is because strings are UTF-8 encoded, and a byte index might not correspond to a valid character boundary.

To iterate over characters, use the chars method:

auto
for c in "hello".chars() {
    print(c.to_string())
}

Storing Keys with Associated Values in Hash Maps ​

The last of our common collections is the hash map. The type Map<K, V> stores a mapping of keys of type K to values of type V. In Auto, Map is available through Rust's standard library using the use.rust directive.

Creating a New Hash Map ​

Creating a new hash map and inserting keys and values

Key differences:

FeatureAutoRust
Importuse.rust std::collections::HashMapuse std::collections::HashMap;
Create emptyHashMap.new()HashMap::new()
Insertmap.insert(key, val)map.insert(key, val)

Accessing Values ​

To get a value from a hash map, use the get method which returns ?V:

auto
use.rust std::collections::HashMap

fn main() {
    var scores = HashMap.new()
    scores.insert("Blue", 10)
    let score = scores.get("Blue") ?? 0
    print(f"Score: ${score}")
}

Updating a Hash Map ​

Overwriting a value is done with another insert call using the same key:

auto
scores.insert("Blue", 25)  // Overwrites the previous value of 10

Summary ​

Auto's collection types provide the same functionality as Rust's, with a more streamlined and safer API:

ConceptAutoRust
Dynamic arrayList<T>Vec<T>
Create listList.new()Vec::new() / vec![]
Add elementv.push(val)v.push(val)
Safe accessv[i] ?? defaultv.get(i).copied().unwrap_or(default)
Iteratefor item in vfor item in v
Hash mapMap<K, V> / HashMapHashMap<K, V>
String concatenationf"${a}, ${b}"format!("{}, {}", a, b)
String appends.push_str("text")s.push_str("text")

The most notable improvement is Auto's safe list indexing — v[i] returns ?T instead of panicking, and the ?? operator provides a clean way to handle missing values. This eliminates an entire class of runtime errors.

In the next chapter, we'll discuss error handling in more depth.

Released under the MIT License.