Using Structs to Structure Related Data
A struct, or structure, is a custom data type that lets you package together and name multiple related values that make up a meaningful group. In Auto, structs are defined using the type keyword instead of Rust's struct keyword, but the concept is the same. If you're familiar with an object-oriented language, a struct is like an object's data attributes.
In this chapter, we'll compare and contrast tuples with structs, demonstrate how to define and instantiate structs, and discuss how to define associated functions and methods using both inline definitions within type blocks and the ext keyword for method extensions.
Defining and Instantiating Structs
Structs are similar to tuples in that both hold multiple related values. Like tuples, the pieces of a struct can be different types. Unlike with tuples, in a struct you'll name each piece of data so it's clear what the values mean.
To define a struct in Auto, we use the type keyword and name the type. Then, inside curly brackets, we define the names and types of the fields. Note that Auto uses space-separated type annotations (name Type) instead of Rust's colon syntax (name: Type).
Key differences from Rust:
| Feature | Auto | Rust |
|---|---|---|
| Define struct | type Name { } | struct Name { } |
| Field type syntax | name Type | name: Type |
| Create instance | Name(val1, val2) | Name { field: val } |
| Mutable instance | var user = Name(...) | let mut user = Name { ... } |
Auto uses positional constructor syntax (User(true, "name", ...)) instead of Rust's named field syntax (User { active: true, ... }). The arguments are matched to fields in the order they are declared.
Mutable Instances
To modify a field, use var to create a mutable instance:
Note that the entire instance must be mutable; Auto doesn't allow us to mark only certain fields as mutable, just like Rust.
An Example Program Using Structs
Let's write a program that calculates the area of a rectangle, starting with simple variables and refactoring toward structs.
Using Separate Variables
The issue with this code is that width and height are related, but the function signature doesn't convey that relationship. Let's refactor using a type.
Refactoring with Types
Now the function signature clearly states that it calculates the area of a Rectangle, and the fields width and height are named and related.
Borrowing with .view
As discussed in Chapter 4, we can use .view to borrow a value without taking ownership:
Methods
Methods are functions defined within the context of a type. In Auto, methods can be defined in two ways:
- Inline methods — defined directly inside a
type { }block - Extension methods — defined in an
extblock outside the type definition
Both approaches generate Rust impl blocks.
Defining Methods Inline
In Auto, you can define methods directly inside the type block. The self parameter is implicit — inside method bodies, use .field to access the instance's fields:
Key differences:
| Feature | Auto | Rust |
|---|---|---|
| Define methods | Inside type { } block | In separate impl block |
| Self parameter | Implicit (not written) | Explicit &self |
| Access own fields | .field | self.field |
The .field syntax is shorthand for self.field. Auto automatically injects &self as the first parameter and converts .field to self.field in the transpiled Rust code.
Methods with Multiple Parameters
You can add additional parameters to methods after the implicit self:
Associated Functions with ext
In Rust, associated functions (like String::from) are defined in impl blocks without self as the first parameter. In Auto, you use the ext keyword to add associated functions and methods to a type after its initial definition:
Key mapping differences for associated functions:
| Feature | Auto | Rust |
|---|---|---|
| Define extension | ext TypeName { } | impl TypeName { } |
| Call associated fn | TypeName.method() | TypeName::method() |
| Constructor call | TypeName(args) | TypeName { field: val } |
The ext keyword is Auto's equivalent of Rust's impl for adding methods and associated functions to types. Unlike impl, ext can be used anywhere in the codebase, not just after the type definition.
Multiple ext Blocks
Each type can have multiple ext blocks. This is useful for organizing methods by functionality:
Tuple-Like Types
Auto doesn't have Rust's "tuple structs" (unnamed fields). Instead, use regular types with named fields, which provide better readability:
Ownership of Type Data
In Auto, just like in Rust, types can own their data (using
String) or borrow it (using.view). When a type owns its data, the data is valid for as long as the entire type instance is valid. Auto's compiler automatically handles lifetime checking without requiring explicit annotations.
Summary
Auto's type keyword provides the same functionality as Rust's struct, with a more streamlined syntax:
| Concept | Auto | Rust |
|---|---|---|
| Define type | type Name { fields } | struct Name { fields } |
| Field syntax | name Type | name: Type |
| Create instance | Name(val1, val2) | Name { field: val } |
| Mutable instance | var x = Name(...) | let mut x = Name { ... } |
| Inline method | Inside type { } block | In impl block |
| Extension method | ext Name { } | impl Name { } |
| Self access | .field | self.field |
| Associated function | ext Name { fn new() } | impl Name { fn new() } |
| Call associated fn | Name.fn() | Name::fn() |
Types and enums (discussed in Chapter 6) are the building blocks for creating new types in your program's domain to take full advantage of Auto's compile-time type checking.
Let's move on to Chapter 6 and look at enums and pattern matching with the is keyword.