Struktury danych
Typy struktur
- Struktura jednostkowa/pusta (ang. unit-like structure)
struct EmptyStructure; fn main() { let es = EmptyStructure; }
- 🤔 Czy i w jakim celu możemy definiować takie struktury?
- Struktura a’la krotka (ang. tuple structure)
struct Color(i32, i32, i32); // fields are without names (are access as in tuple) struct Point(i32, i32, i32); // a different structure with the same types fn main() { let black = Color(0, 0, 0); let origin = Point(0, 0, 0); }
- Po prostu struktura 😈:
struct Point { x : f32, // named attributes like in classes y : f32, }
Tworzenie instancji struktury
-
W przykładowym projekcie, w katalogu
examples
dodaj plikrectangle.rs
. -
Otwórz powyższy plik, a w nim utwórz strukturę danych opisującą prostokąt:
#[derive(Debug)] // allows to print the structure in debug mode (ie. to use {:?}) struct Rectangle { x : f32, y : f32 }
-
Dodaj metodę
main
, w której utwórz nową instancję strukturyRectangle
i wypisz ją na konsole:fn main() { let r = Rectangle{x: 1.0, y: 2.0}; // create a new instance using constructor println!("{:?}", r); println!("x: {}, y: {}", r.x, r.y); // access particular fields using . operator }
-
Skompiluj kod i uruchom przykład:
cargo run --example rectangle
-
Możesz również utworzyć instancje na bazie innej instancji (poniższy kod dopisz do funkcji
main
z poprzedniego punktu)let r2 = Rectangle{x : 5, ..r1} // the rest of r2 parameters are copied from r1 println!("{:?}", r); println!("x: {}, y: {}", r2.x, r2.y);
Atrybuty struktur danych, w kontekście posiadania wartości, zachowują się w analogicznych sposób jak zmienne.
Jeśli atrybut jest typu, który posiada daną wartość (ang. owned type), to właśność tej wartości należy do instancji struktury. Oznacza to, że wartość ta zostanie usunięta z pamięci w sytuacji, kiedy instancja struktury wyjdzie poza zakres.
Jeśli natomist atrybut struktury jest referencją, to konieczne będzie określenie czasu życia takiej wartości - o czym będzie więcej w sekcji Czas życia.
Dostęp i modyfikacja atrybutów
- Instancja niemutowalna - wszystkie atrybuty są niemutowalne
let r3 = Rectangle{x : 5.0, y : 9.0}; println!("[{}, {}]", r3.x, r3.y);
- Próba ustawienia wartości atrubutu
r3.x = 6.0; // error
- Instacja mutowalna
let mut r4 = Rectangle{x : 5.0, y : 9.0}; println!("[{}, {}]", r4.x, r4.y); r4.x = 6.0; r4.y = 7.0; println!("[{}, {}]", r4.x, r4.y);
Mutowalność struktury (na poziomie tworzenia nowej instancji). Nie można ustawiać mutowalności pojedynczych pól struktury.
Metody
Metody instancyjne
-
Pod definicją struktury
Rectangle
dodaj blok będący miejscem implementacji metod związanych z naszą strukturąimpl Rectangle { // the place for rectangle methods }
-
Wewnętz powyższego bloku zdefiniuje metodę obliczającą pole prostokąta.
fn area(&self) -> f32 { self.x * self.y }
Zwróć uwage na pierwszy parametr metody (
&self
), który oznacza referencję do instancji struktury (musi to być zawsze pierwszy parametr metod instancyjnej). Referencjęself
możesz wykorzystać w ciele metody, aby odwołać się do pól instancji struktury lub innych metod instancyjnych. -
W funkcji
main
dodaj kod, który wywoła metodęarea
na stworzonej wcześniej instrancjiRectangle
println!("Area of {:?} is {}", r, r.area());
-
W analogiczny sposób dodaj metodę wyznaczającą obwód prostokąta.
-
Dodaj metodę umożliwiającą przeskalowanie prostokąta o zadany współczynnik.
fn scale(&mut self, factor:f32) { self.x = self.x * factor; self.y = self.y * factor; }
Zwróć uwagę na pierwszy parametr metody (
&mut self
), który oznacza, że metoda ta będzie modyfikować instancję struktury. -
W metodzie
main
utwórz nową instancję i wywołaj na nią metodęscale
:let r = Rectangle{ x : 5.0, y : 4.0}; r.scale(2.0); println!("Area of r is {}", r.area());
-
Czy kod się skompilował? Dlaczego?
-
Jak poprawić powyższy przykład?
Metody powiązane
- W bloku
impl
utwórz metodęnew_square
, który utworzy nowy kwadrat (metody tego typu nazywane są metodami fabrycznymi). Blok ten powinien wyglądać następująco:impl Rectangle { // ... other methods fn new_square(x : f32) -> Rectangle { // there is no self in the argument list Rectangle{x : x, y : x} } }
- Metody powiązane wywołujemy za pomocą nazwy struktury, a następnie dwóch znaków dwukropka, w naszym przypadku będzie to
Rectangle::new_square
. W metodziemain
utwórz nowy kwadrat i wydrukuj “go” na konsolę:let square = Rectangle::new_square(5.0); println!("square: {:?}", square);
- Możesz uprościć metodę
new_square
wykorzystując regułę, pozwalającą na automatyczne przypisane parametru funkcji do parametru tworzonej instancji struktury o tej samej nazwie:fn new_square(x : f32) -> Rectangle { Rectangle{x, y : x} // the first argument can be passed in simplified form }