ixa 2.0.0-beta2

A framework for building agent-based models
Documentation
# Learning Rust for Ixa

## Why Rust?

We designed Ixa to be efficient for computers and for people. Rust provides the
speed and memory characteristics necessary for large-scale,
computationally-intensive simulations while still being relatively friendly to
work with. That being said, if you are used to working in a higher-level
language like R or Python, Rust can take some time to learn. We recommend taking
a look at the [The Rust Book](https://rust-book.cs.brown.edu/) for a
comprehensive overview. Here are a few features that might be new to you:

1. Rust has a strict model of how it uses your computer memory, called
   ownership. In practice, this means that you can only manipulate objects in
   specific ways. This helps code actually do what you think it is doing and has
   been shown to reduce
   [long-term bugs]https://thehackernews.com/2024/09/googles-shift-to-rust-programming-cuts.html#:~:text=Google%20has%20revealed%20that%20its,a%20period%20of%20six%20years..
   Rust's ownership rules are enforced at _compile-time_, which helps you find
   errors in your code earlier. When developing ABMs with Ixa, these
   capabilities help us more easily reason about complicated model logic and
   ensure that plugins interact modularly.

2. Rust has a cohesive ecosystem of all the tools you will need for development:
   it has a built-in package/project manager (`cargo`), a built-in linter
   (`clippy`), a built-in environment manager for updating Rust versions so that
   you can manage multiple simultaneously (`rustup`), and a global repository
   for all [packages]https://crates.io. Rust provides a complete ecosystem for
   all your model development needs, and we find this centralization useful for
   ensuring robust version control and reproducibility of code across different
   users and computers. This also means that chances are someone has built a
   crate for a problem you may be troubleshooting.

3. Rust is a strongly-typed language, meaning that the type of each variable
   must be known at compile-time and cannot be changed after variable
   definition. However, Rust also has automatic type inference and other
   ergonomic features to help make writing code more seamless and efficient. As
   a result, you only need to specify the type of a variable in cases where it
   is ambiguous while still taking advantage of the rigidity of static typing to
   ensure your code is doing what you think it is doing. We find the combination
   of static typing with other Rust features for quick prototyping valuable for
   writing models and tests simultaneously.

## Common Rust patterns in Ixa modeling

[The Rust Book](https://rust-book.cs.brown.edu/) remains the best resource to
learn Rust, and we recommend reading the book in its entirety. However, the book
emphasizes concepts that are less prevalent in day-to-day Ixa use (for instance,
ownership), and certain patterns that pop up a lot are less emphasized. Below,
we include sections of the book that are particularly important.

1. [Chapter 5: Structs]https://rust-book.cs.brown.edu/ch05-00-structs.html: If
   you are new to object-oriented programming, this chapter introduces the
   "object-oriented"-ness of Rust. Rust has elements of both an object-oriented
   and functional programming language, but structs come up often when writing
   an Ixa model, and thinking about Ixa as being an object-oriented framework is
   useful for evaluating the kinds of data you need in your model and how to use
   the data.

2. [Chapter 6: Enums]https://rust-book.cs.brown.edu/ch06-00-enums.html: Enums,
   match statements, and storing data in enum variants are all distinctly Rust
   patterns. If you have previously worked with Haskell or OCaml, these objects
   may be familiar to you. Nevertheless, enums come up frequently in Ixa, in
   particular with person properties (e.g., a person's infection status is a
   value of an enum with variants `Susceptible`, `Infected`, `Recovered`), and
   seeing how you can store information in each of these variants helps modelers
   understand how to best store the data they need for their particular use
   case.

3. [Chapter 10, Section 2: Traits]https://rust-book.cs.brown.edu/ch10-02-traits.html:
   _If you read_ _only one section, let this be it._ Traits are the cornerstone
   of Ixa's modular nature. They are used in at least two ways throughout the
   Ixa codebase. First, the methods that make up higher-level abstractions --
   like `Property` or `Event` -- are organized into traits, giving them shared
   features so that calling code can use these methods without knowing the exact
   underlying type. For instance, your model might define a trait to specify
   interventions that impact the probability of transmission -- like facemasks
   or hazmat suits. Regardless of the exact nature of the intervention, your
   model wants to call some methods on the transmisison modifier, like returning
   its efficacy. If these methods are grouped into the `TransmissionModifier`
   trait, they can be implemented on any type and used wherever the code needs a
   transmission modifier without depending on the underlying type. Secondly,
   traits implemented on `Context` (called "Context extensions" in Ixa) are the
   primary way of new modules adding a public interface so that their methods
   can be called from other modules. For instance, the `ContextPeopleExt` trait
   extension provides methods relating to people and their properties. Rust
   traits are a supercharged version of "interfaces" in other programming
   languages, and thinking in terms of traits will help you write modular Ixa
   code.

4. [Chapter 13, Section 1: Closures]https://rust-book.cs.brown.edu/ch13-01-closures.html:
   Ixa often requires the user to specify a function that gets executed in the
   future. This chapter goes over the mechanics for how anonymous functions are
   specified in Rust. Although the syntax is not challenging, this chapter
   discusses capturing and moving values into a closure, type inference for
   closure arguments, and other concepts specific to Rust closures. You may see
   the word "callback" referred to in the Ixa documentation -- callbacks are
   what Ixa calls the user-specified closures that are evaluated when executing
   plans or handling events.

In addition,
[Chapter 19: Patterns](https://rust-book.cs.brown.edu/ch19-00-patterns.html)
reiterates the power of enum variants and the `match` statement in Rust. If you
find yourself writing more advanced Rust code, Chapters
[9: Error Handling](https://rust-book.cs.brown.edu/ch09-00-error-handling.html),
[18: Object-oriented programming](https://rust-book.cs.brown.edu/ch18-00-oop.html),
and
[20: Advanced Features](https://rust-book.cs.brown.edu/ch20-00-advanced-features.html)
include helpful tips as you dive into more complicated Ixa development.

If you find yourself writing more analysis-focused code in Rust, Chapters
[9: Error Handling](https://rust-book.cs.brown.edu/ch09-00-error-handling.html)
and [13.2: Iterators](https://rust-book.cs.brown.edu/ch13-02-iterators.html)
include helpful tools for writing code reminiscent of Python.

### On Ownership

Rust's signature feature is its ownership rules. We tried to design Ixa to
handle ownership internally, so that users rarely have to think about the
specific Rust rules when interfacing with Ixa. Nevertheless, understanding
ownership rules is valuable for debugging more complicated Ixa code and
understanding what you can and cannot do in Rust. There are excellent resources
for learning Rust's ownership rules available
[online](https://educatedguesswork.org/posts/memory-management-4/), but at a
high-level, here are the key ideas to understand:

1. When a function takes an object as input, it takes ownership of that object
   -- meaning that the object is "consumed" by the function and does not exist
   after the calling of that function. - This is true for all types except those
   that are automatically copyable, or in Rust lingo implement the `Copy` trait.
   All primitive types -- floats, integers, booleans, etc. -- implement `Copy`,
   meaning that this rule is most commonly felt with vectors and hashmaps. These
   are examples of types that do not implement `Copy` -- in this case, that is
   because their size dynamically changes as data is added and removed, so Rust
   stores them in a part of the memory optimized for changing sizes rather than
   fast access and copying.
2. References (denoted by an `&` in front of an object like `&Object`) allow
   functions to have access to the object without taking ownership of it.
   Therefore, we often return references to an object so that we do not have to
   give up explicit ownership of that object, such as when we want to get the
   value of data owned centrally by the simulation (ex., model parameters) in a
   particular module.
3. There are two kinds of references -- mutable references `&mut Object` and
   immutable references `&Object`. Depending on what kind of reference you have,
   you can do one of two kinds of things. If you have an active immutable
   reference to an object, you can take any number of additional immutable
   references to the object. But, if you have a mutable reference to an object,
   you can only ever have that one mutable reference to the object be active.
   This is because an active immutable reference to the object would also have
   changed as the mutable reference is mutated, and Rust can no longer make
   guarantees about the memory to which the immutable reference points.

In practice, #3 often requires the most troubleshooting to get around.
Sometimes, a refactor of your code can help circumvent ownership challenges.

## Ixa tutorials

Within the Ixa repo, we have created some examples. Each example has a readme
that walks the user through a toy model and what it illustrates about Ixa's core
functionality. To run the examples, from the repo root, just specify the name of
the example:

```bash
cargo run --example {name-of-example}
```

In general, you will be using `cargo` to run and interact with your Ixa models
from the command line. We recommend learning some
[basic `cargo` commands](https://doc.rust-lang.org/cargo/guide/index.html), and
there are valuable
[cheatsheets](https://kapeli.com/cheat_sheets/Cargo.docset/Contents/Resources/Documents/index)
to keep handy as you get more involved in active development.

Here are a few other useful commands to know:

- `cargo test` will run all tests in the project.
- `cargo build --release` compiles the project into a shell executable that you
  can ship to your users.
- `cargo add {crate-name}` adds a Rust crate/project dependency to your project
  for you to use.

## Additional Rust resources

- **[Rust By Example]https://doc.rust-lang.org/rust-by-example/index.html:**
  "RBE shows off a bunch of code, and keeps the talking to a minimum. It also
  includes exercises!"
- **[Tour of Rust]https://tourofrust.com/TOC_en.html:** Live code and
  explanations, side by side.
- **[Rust in Easy English]https://dhghomon.github.io/easy_rust/Chapter_3.html:**
  60+ concepts, simple English, example-driven.
- **[Rust for the Polyglot Programmer]https://www.chiark.greenend.org.uk/~ianmdlvl/rust-polyglot/index.html:**
  A guide for the experienced programmer.
- **[The Rust Standard Library Documentation]https://doc.rust-lang.org/std/index.html**
- **[The Cargo Book]https://doc.rust-lang.org/cargo/index.html:** An online
  manual for Rust's package manager Cargo.
- **[The Rust Playground]https://play.rust-lang.org/:** Execute sample code in
  your browser.