Reference / Classes
Classes
A class is a type whose values are references to objects. All objects of a class have the same fields and methods.
class Counter {
name: string
mut hits: i64
fn hit(self) {
self.hits += 1
}
fn report(self) -> string {
return "{self.name}: {self.hits}"
}
}
fn main() {
let door = Counter(name: "door", hits: 0)
door.hit()
door.hit()
print(door.report()) // door: 2
}
An object is shared. A binding, an argument or a field that holds an object refers to that object, and does not copy it (Values and memory).
Declarations
A class declaration is class, the name, and a body in { and }. The body holds the fields and the methods of the class, in any order.
class Point {
x: i64
y: i64
fn sum(self) -> i64 {
return self.x + self.y
}
}
class Empty {}
fn main() {
print(Point(x: 3, y: 4).sum()) // 7
let _e = Empty()
}
The rules:
- A field is
name: Type, ormut name: Typefor a field that can change. It has nolet(E0016). - A line break separates two fields. A
;can separate them too, but a,cannot (E0015). Soclass A { v: i64 }is valid. - A class is declared at the top level. Its name follows the rules of a function name (Functions).
- Two fields, two methods, or a field and a method of one class cannot have the same name (E0034).
- A class cannot extend another class. Sisu has no inheritance.
- A class that no live code builds builds with a warning (W0012). A field that no live code reads builds with a warning (W0013). A name that starts with
_gets no warning. - A generic class also has type parameters after its name, such as
class Stack<T>(Generics).
A field cannot need an object of its own class, directly or through other classes or enums, because no first object could exist (E0027). An optional field, such as next: Node?, or an array field, such as children: [Node], can be empty, so it is valid. An enum with a variant that needs no object of the class, such as a variant with no payload, also ends the chain.
class Node {
value: i64
next: Node // error[E0027]
}
fn main() {
print(1)
}
E0027: what this error means
Constructors
A constructor Name(field: value, ...) makes a new object of the class Name. It gives a value to each field.
class Sauna {
name: string
mut heat: i64
}
fn main() {
let s = Sauna(name: "lake", heat: 80)
print("{s.name} {s.heat}") // lake 80
}
The rules:
- A constructor names each field once, with its label, in the order of the declaration. A field has no default value.
- A missing field is an error (E0056). So is an argument after the last field (E0064), and an argument without a label (E0065).
- Fields in a different order are an error (E0066). A label that is not a field of the class is an error too (E0063).
- Each value must have the type of its field. For an optional field
T?, aTbecomes aT?(Types). - The program calculates the arguments from left to right, which is the order of the fields (Order of evaluation).
- Each call of a constructor makes a new object, also with the same values.
class Point {
x: i64
y: i64
}
fn main() {
let p = Point(y: 2, x: 1) // error[E0066]
}
E0066: what this error means
Fields
x.name reads the field name of the object x (Fields). A field without mut gets its value from the constructor and never changes. A mut field can change.
class Player {
name: string
mut score: i64
}
fn main() {
let p = Player(name: "Aino", score: 0)
p.score += 10
print(p.score) // 10
print(p.name) // Aino
}
The rules:
- An assignment to a field without
mutis an error, also in a method (E0001). - An assignment to a
mutfield changes the object. The binding that holds the object does not needmut(Assignment). - The object can come from any expression, such as a parameter, an element of an array, a field of another object, or
self. - An assignment cannot go through
?.(E0022). - A
let mutbinding of an object must change by an assignment to the binding itself. A change to a field of its object does not count (W0002). - To write an element of an array in a field, the field must be
mut(E0001, Arrays). - For an optional object, read a field with
?.(Optional chaining).
class Box {
mut value: i64
}
fn fill(b: Box) {
b.value = 7
}
fn main() {
let boxes = [Box(value: 1), Box(value: 2)]
fill(boxes[0])
boxes[1].value = 9
print(boxes[0].value + boxes[1].value) // 16
}
class Bag {
items: [i64]
}
fn main() {
let bag = Bag(items: [1, 2])
bag.items[0] = 5 // error[E0001]
}
E0001: what this error means
Methods
A method is a function in the body of a class. Its first parameter is self, the object that the method is called on. A method call x.name(...) runs the method with x as self (Method calls).
class Account {
owner: string
mut balance: i64
fn deposit(self, amount: i64) {
self.balance += amount
}
fn show(self) -> string {
return "{self.owner}: {self.balance}"
}
}
fn main() {
let a = Account(owner: "Eero", balance: 5)
a.deposit(10)
print(a.show()) // Eero: 15
}
The rules:
selfis the first parameter of each method (E0017, E0020). It isself, nevermut self(E0018).selfis valid only in a method (E0041). An assignment toselfis an error (E0073).- A method can write the
mutfields ofself. The change shows through every reference to the object. - In a method, use
selfto reach the fields and the other methods:self.xandself.m(). A barexorm()does not find a field or a method. A barexfinds a binding or a constant with that name. A barem(...)finds a function, a class (its constructor) or a conversion, such asf64(n). Else a barexis an error (E0033). A barem(...)is also an error: E0070 when a method of the class has that name (writeself.m(...)), or when the first argument has a method of that name. Else it is E0052. - A method can call a method that is declared below it, and it can call itself.
- A method can take the name of a function, such as
lenorprint. A callx.print()finds the method, andprint(...)finds the function. - A method is not a function. A call
name(x)does not find the methodname: writex.name()(E0070). A call on the class, such asAccount.show(), is an error (E0105). - For an optional object, call a method with
?.(Optional chaining).
A method can return self, so that calls can follow one another.
class Path {
mut steps: i64
fn step(self) -> Path {
self.steps += 1
return self
}
}
fn main() {
let p = Path(steps: 0)
print(p.step().step().steps) // 2
}
class Counter {
mut n: i64
fn reset(self) {
n = 0 // error[E0033]
}
}
fn main() {
Counter(n: 1).reset()
}
E0033: what this error means
Identity
Each object has an identity. a is b is true when a and b refer to the same object (is). A copy of a reference refers to the same object, and a constructor makes a new one.
class Sauna {
mut heat: i64
}
fn main() {
let a = Sauna(heat: 80)
let b = a
let c = Sauna(heat: 80)
b.heat = 90
print(a.heat) // 90
print(a is b) // true
print(a is c) // false
}
An object stays in memory while a reference to it exists. Values and memory tells when Sisu frees it.
Interfaces In development
A class can name interfaces after its name, as in class Person: Named. Its body then holds the methods of those interfaces (Interfaces).
interface Named {
fn name(self) -> string
}
class Person: Named {
first: string
fn name(self) -> string {
self.first
}
}
fn main() {
print(Person(first: "Aino").name()) // Aino
}
Sisu