Primitive Types
Simplex provides several built-in primitive types:
| Type | Description | Example |
|---|---|---|
i64 |
64-bit signed integer | 42, -100 |
f64 |
64-bit floating point | 3.14, -0.5 |
bool |
Boolean | true, false |
String |
UTF-8 text | "hello" |
char |
Unicode character | 'a', '@' |
Structs
Structs let you group related data together:
struct Person {
name: String,
age: i64,
email: String
}
fn main() {
// Create an instance
let alice = Person {
name: "Alice",
age: 30,
email: "alice@example.com"
}
print("{alice.name} is {alice.age} years old")
}
Methods on Structs
You can add methods to structs:
struct Rectangle {
width: f64,
height: f64
}
impl Rectangle {
// Constructor
fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
// Method using self
fn area(self) -> f64 {
self.width * self.height
}
fn perimeter(self) -> f64 {
2.0 * (self.width + self.height)
}
}
fn main() {
let rect = Rectangle::new(10.0, 5.0)
print("Area: {rect.area()}")
}
Enums
Enums define a type by enumerating its possible variants:
enum Status {
Pending,
Processing,
Completed,
Failed(String) // Variant with data
}
fn describe_status(status: Status) -> String {
match status {
Status::Pending => "Waiting to start",
Status::Processing => "In progress",
Status::Completed => "All done!",
Status::Failed(reason) => "Failed: {reason}"
}
}
fn main() {
let status = Status::Failed("Connection timeout")
print(describe_status(status))
}
Option and Result
Two essential built-in enums for handling absence and errors:
// Option: A value that might not exist
fn find_user(id: i64) -> Option<String> {
if id == 1 {
Some("Alice")
} else {
None
}
}
// Result: An operation that might fail
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err("Cannot divide by zero")
} else {
Ok(a / b)
}
}
fn main() {
// Handling Option
match find_user(1) {
Some(name) => print("Found: {name}"),
None => print("User not found")
}
// Handling Result with ? operator
let result = divide(10.0, 2.0)?
print("Result: {result}")
}
The ? Operator
The ? operator automatically propagates errors. If a Result
is Err, it returns early from the function. This makes error handling
concise and readable.
Pattern Matching
Pattern matching is a powerful way to destructure and inspect values:
struct Point { x: f64, y: f64 }
fn describe_point(p: Point) -> String {
match p {
Point { x: 0.0, y: 0.0 } => "Origin",
Point { x: 0.0, y } => "On Y-axis at {y}",
Point { x, y: 0.0 } => "On X-axis at {x}",
Point { x, y } if x == y => "On diagonal at {x}",
Point { x, y } => "At ({x}, {y})"
}
}
fn main() {
print(describe_point(Point { x: 0.0, y: 5.0 }))
print(describe_point(Point { x: 3.0, y: 3.0 }))
}
Generics
Write functions and types that work with any type:
// Generic struct
struct Pair<T> {
first: T,
second: T
}
// Generic function
fn swap<T>(pair: Pair<T>) -> Pair<T> {
Pair {
first: pair.second,
second: pair.first
}
}
fn main() {
let numbers = Pair { first: 1, second: 2 }
let swapped = swap(numbers)
print("{swapped.first}, {swapped.second}") // 2, 1
let strings = Pair { first: "hello", second: "world" }
let swapped = swap(strings)
print("{swapped.first}, {swapped.second}") // world, hello
}
Try It Yourself
Create an enum called Shape with variants for Circle(radius),
Rectangle(width, height), and Triangle(base, height).
Write a function that calculates the area for each shape using pattern matching.
Summary
In this tutorial, you learned:
- Primitive types in Simplex
- Creating structs and adding methods with
impl - Defining enums with variants that can hold data
- Using
OptionandResultfor safe handling - Pattern matching with
matchexpressions - Writing generic functions and types
In the next tutorial, we'll explore Simplex's actor model for building concurrent and distributed systems.