18 KiB
Βασικά της Rust
Γενικοί Τύποι
Δημιουργήστε ένα struct όπου μία από τις τιμές του μπορεί να είναι οποιοσδήποτε τύπος
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
pub fn new(value: T) -> Self {
Wrapper { value }
}
}
Wrapper::new(42).value
Wrapper::new("Foo").value, "Foo"
Option, Some & None
Ο τύπος Option σημαίνει ότι η τιμή μπορεί να είναι τύπου Some (υπάρχει κάτι) ή None:
pub enum Option<T> {
None,
Some(T),
}
Μπορείτε να χρησιμοποιήσετε συναρτήσεις όπως is_some()
ή is_none()
για να ελέγξετε την τιμή της Option.
Μακροεντολές
Οι μακροεντολές είναι πιο ισχυρές από τις συναρτήσεις επειδή διευρύνονται για να παράγουν περισσότερο κώδικα από αυτόν που έχετε γράψει χειροκίνητα. Για παράδειγμα, η υπογραφή μιας συνάρτησης πρέπει να δηλώσει τον αριθμό και τον τύπο των παραμέτρων που έχει η συνάρτηση. Οι μακροεντολές, από την άλλη πλευρά, μπορούν να πάρουν μεταβλητό αριθμό παραμέτρων: μπορούμε να καλέσουμε την println!("hello")
με ένα όρισμα ή την println!("hello {}", name)
με δύο ορίσματα. Επίσης, οι μακροεντολές διευρύνονται πριν ο μεταγλωττιστής ερμηνεύσει τη σημασία του κώδικα, έτσι μια μακροεντολή μπορεί, για παράδειγμα, να υλοποιήσει ένα trait σε έναν συγκεκριμένο τύπο. Μια συνάρτηση δεν μπορεί, επειδή καλείται κατά την εκτέλεση και ένα trait πρέπει να υλοποιηθεί κατά τη μεταγλώττιση.
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
($val:expr) => {
println!("Look at this other macro: {}", $val);
}
}
fn main() {
my_macro!();
my_macro!(7777);
}
// Export a macro from a module
mod macros {
#[macro_export]
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
}
Επανάληψη
Η επανάληψη είναι μια σημαντική έννοια στην προγραμματισμό. Σας επιτρέπει να εκτελέσετε ένα σύνολο εντολών επαναλαμβανόμενα, μέχρι να ικανοποιηθεί μια συγκεκριμένη συνθήκη. Στη γλώσσα προγραμματισμού Rust, υπάρχουν διάφοροι τρόποι για να επαναλάβετε κώδικα, ανάλογα με τις ανάγκες σας.
Επανάληψη με τη χρήση της loop
Η εντολή loop
εκτελεί μια σειρά εντολών επαναλαμβανόμενα για αόριστο χρονικό διάστημα. Για να διακόψετε την επανάληψη, μπορείτε να χρησιμοποιήσετε την εντολή break
όταν μια συγκεκριμένη συνθήκη ικανοποιηθεί.
loop {
// Κώδικας που θα εκτελείται επαναλαμβανόμενα
if condition {
break;
}
}
Επανάληψη με τη χρήση της while
Η εντολή while
εκτελεί μια σειρά εντολών επαναλαμβανόμενα όσο μια συγκεκριμένη συνθήκη είναι αληθής.
while condition {
// Κώδικας που θα εκτελείται επαναλαμβανόμενα
}
Επανάληψη με τη χρήση της for
Η εντολή for
εκτελεί μια σειρά εντολών επαναλαμβανόμενα για κάθε στοιχείο μιας συλλογής, όπως ένας πίνακας ή ένας διάνυσμα.
for element in collection {
// Κώδικας που θα εκτελείται επαναλαμβανόμενα για κάθε στοιχείο
}
Αυτοί είναι οι βασικοί τρόποι επανάληψης στη γλώσσα προγραμματισμού Rust. Επιλέξτε τον κατάλληλο τρόπο επανάληψης ανάλογα με τις ανάγκες του προγράμματός σας.
// Iterate through a vector
let my_fav_fruits = vec!["banana", "raspberry"];
let mut my_iterable_fav_fruits = my_fav_fruits.iter();
assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana"));
assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry"));
assert_eq!(my_iterable_fav_fruits.next(), None); // When it's over, it's none
// One line iteration with action
my_fav_fruits.iter().map(|x| capitalize_first(x)).collect()
// Hashmap iteration
for (key, hashvalue) in &*map {
for key in map.keys() {
for value in map.values() {
Αναδρομικό Κουτί
Το αναδρομικό κουτί είναι ένα χρήσιμο εργαλείο στη γλώσσα προγραμματισμού Rust που μας επιτρέπει να αναφερόμαστε σε μια τιμή που αναφέρεται στον εαυτό της. Αυτό μας επιτρέπει να δημιουργούμε δομές δεδομένων που περιέχουν αναφορές στον εαυτό τους, δημιουργώντας έτσι αναδρομικές δομές.
Για να δημιουργήσουμε ένα αναδρομικό κουτί στη Rust, χρησιμοποιούμε τον τύπο δεδομένων Box
. Ο τύπος Box
είναι ένας έξυπνος δείκτης που αναλαμβάνει την αποδέσμευση της μνήμης όταν δεν χρειάζεται πλέον.
Για παράδειγμα, μπορούμε να δημιουργήσουμε ένα αναδρομικό κουτί που περιέχει έναν ακέραιο αριθμό ως εξής:
fn main() {
let recursive_box: Box<i32> = Box::new(42);
println!("Value: {}", recursive_box);
}
Στο παραπάνω παράδειγμα, δημιουργούμε ένα αναδρομικό κουτί με την τιμή 42 και το εκτυπώνουμε. Η μνήμη που καταλαμβάνει το αναδρομικό κουτί απελευθερώνεται αυτόματα όταν δεν υπάρχουν πλέον αναφορές σε αυτό.
Το αναδρομικό κουτί είναι ένα ισχυρό εργαλείο που μας επιτρέπει να δημιουργούμε πολύπλοκες δομές δεδομένων και αλγορίθμους στη Rust. Χρησιμοποιήστε το με προσοχή και κατανόηση των αναγκών σας για να αποφύγετε προβλήματα με τη μνήμη.
enum List {
Cons(i32, List),
Nil,
}
let list = Cons(1, Cons(2, Cons(3, Nil)));
Συνθήκες
if
Η δομή ελέγχου if
χρησιμοποιείται για να εκτελέσει ένα τμήμα κώδικα μόνο αν μια συγκεκριμένη συνθήκη είναι αληθής. Η σύνταξη της δομής if
είναι η εξής:
if condition {
// Κώδικας που εκτελείται αν η συνθήκη είναι αληθής
}
Μπορείτε επίσης να προσθέσετε μια δομή else
για να εκτελέσετε έναν διαφορετικό κώδικα αν η συνθήκη είναι ψευδής:
if condition {
// Κώδικας που εκτελείται αν η συνθήκη είναι αληθής
} else {
// Κώδικας που εκτελείται αν η συνθήκη είναι ψευδής
}
Μπορείτε επίσης να χρησιμοποιήσετε τη δομή else if
για να ελέγξετε περισσότερες από μία συνθήκες:
if condition1 {
// Κώδικας που εκτελείται αν η συνθήκη 1 είναι αληθής
} else if condition2 {
// Κώδικας που εκτελείται αν η συνθήκη 2 είναι αληθής
} else {
// Κώδικας που εκτελείται αν καμία από τις προηγούμενες συνθήκες δεν είναι αληθής
}
let n = 5;
if n < 0 {
print!("{} is negative", n);
} else if n > 0 {
print!("{} is positive", n);
} else {
print!("{} is zero", n);
}
αντιστοίχιση
The match
expression in Rust is used to compare a value against a series of patterns and execute the corresponding code block for the first matching pattern. It is similar to a switch statement in other programming languages.
match value {
pattern1 => {
// code block to execute if value matches pattern1
},
pattern2 => {
// code block to execute if value matches pattern2
},
// more patterns...
_ => {
// code block to execute if value does not match any pattern
}
}
The _
pattern is a catch-all pattern that matches any value. It is commonly used as the last pattern to handle all remaining cases.
The match
expression is powerful and flexible, allowing for complex pattern matching and exhaustive handling of all possible cases. It is often used in Rust to replace lengthy if-else chains and improve code readability.
match number {
// Match a single value
1 => println!("One!"),
// Match several values
2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
// TODO ^ Try adding 13 to the list of prime values
// Match an inclusive range
13..=19 => println!("A teen"),
// Handle the rest of cases
_ => println!("Ain't special"),
}
let boolean = true;
// Match is an expression too
let binary = match boolean {
// The arms of a match must cover all the possible values
false => 0,
true => 1,
// TODO ^ Try commenting out one of these arms
};
βρόχος (άπειρος)
Ο βρόχος (loop) είναι μια δομή προγραμματισμού που επαναλαμβάνει μια συγκεκριμένη ενέργεια για έναν αόριστο αριθμό φορών. Αυτό σημαίνει ότι η ενέργεια θα εκτελείται συνεχώς μέχρι να δοθεί μια εντολή για να σταματήσει. Ο άπειρος βρόχος είναι ένας τύπος βρόχου που δεν έχει καμία συνθήκη για να τερματίσει και εκτελείται για πάντα, εκτός αν διακοπεί από τον χρήστη ή από κάποιο σφάλμα στο πρόγραμμα.
loop {
count += 1;
if count == 3 {
println!("three");
continue;
}
println!("{}", count);
if count == 5 {
println!("OK, that's enough");
break;
}
}
ενώ
The while
statement in Rust is used to create a loop that will continue executing as long as a certain condition is true. The syntax for the while
statement is as follows:
while condition {
// code to be executed
}
The condition
is a boolean expression that determines whether the loop should continue or not. If the condition is true, the code inside the loop will be executed. Once the code is executed, the condition will be checked again. If the condition is still true, the code will be executed again, and this process will continue until the condition becomes false.
Here is an example of how the while
statement can be used in Rust:
let mut count = 0;
while count < 5 {
println!("Count: {}", count);
count += 1;
}
In this example, the loop will continue executing as long as the value of count
is less than 5. The value of count
will be printed to the console, and then incremented by 1. This process will repeat until count
reaches 5, at which point the condition will become false and the loop will terminate.
let mut n = 1;
while n < 101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
n += 1;
}
για
for n in 1..101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else {
println!("{}", n);
}
}
// Use "..=" to make inclusive both ends
for n in 1..=100 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
// ITERATIONS
let names = vec!["Bob", "Frank", "Ferris"];
//iter - Doesn't consume the collection
for name in names.iter() {
match name {
&"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
//into_iter - COnsumes the collection
for name in names.into_iter() {
match name {
"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
//iter_mut - This mutably borrows each element of the collection
for name in names.iter_mut() {
*name = match name {
&mut "Ferris" => "There is a rustacean among us!",
_ => "Hello",
}
}
if let
Η δομή if let
στη Rust είναι μια συντομευμένη μορφή της match
που χρησιμοποιείται για να ελέγξει αν μια τιμή ταιριάζει με ένα συγκεκριμένο πρότυπο και να εκτελέσει κώδικα μόνο αν η ταιριάσουσα τιμή αντιστοιχεί στο πρότυπο. Αυτό είναι χρήσιμο όταν ενδιαφερόμαστε μόνο για ένα συγκεκριμένο πρότυπο και δεν μας ενδιαφέρει να καλύψουμε όλες τις πιθανές περιπτώσεις.
Η σύνταξη της if let
είναι η εξής:
if let Some(value) = optional_value {
// Κώδικας που εκτελείται αν η optional_value είναι Some(value)
} else {
// Κώδικας που εκτελείται αν η optional_value είναι None
}
Στο παραπάνω παράδειγμα, ο κώδικας εκτελείται μόνο αν η optional_value
είναι Some(value)
, δηλαδή αν η τιμή είναι μια αποθηκευμένη τιμή και όχι None
. Αν η optional_value
είναι None
, τότε εκτελείται ο κώδικας που βρίσκεται μέσα στην else
παράγραφο.
Η if let
μπορεί να χρησιμοποιηθεί με οποιοδήποτε πρότυπο που ταιριάζει με τον τύπο της τιμής που ελέγχεται. Μπορεί επίσης να χρησιμοποιηθεί με πολλαπλά πρότυπα χρησιμοποιώντας τον τελεστή |
.
let optional_word = Some(String::from("rustlings"));
if let word = optional_word {
println!("The word is: {}", word);
} else {
println!("The optional word doesn't contain anything");
}
while let
Ο όρος while let
χρησιμοποιείται για να εκτελεί μια επανάληψη ενώ μια τιμή παραμένει μια συγκεκριμένη τιμή. Αυτό είναι χρήσιμο όταν θέλουμε να εξετάσουμε μια τιμή και να εκτελέσουμε κώδικα μόνο αν η τιμή αντιστοιχεί σε ένα συγκεκριμένο πρότυπο.
Η σύνταξη του while let
είναι η εξής:
while let Some(pattern) = optional_value {
// Κώδικας που εκτελείται όταν η τιμή αντιστοιχεί στο πρότυπο
}
Στο παραπάνω παράδειγμα, ο κώδικας εκτελείται μόνο όταν η optional_value
είναι Some
και η τιμή της αντιστοιχεί στο pattern
. Αν η optional_value
είναι None
ή η τιμή της δεν αντιστοιχεί στο pattern
, η επανάληψη τερματίζεται.
Ο όρος while let
μπορεί να χρησιμοποιηθεί με οποιοδήποτε τύπο δεδομένων που υποστηρίζει το πρότυπο που καθορίζεται.
let mut optional = Some(0);
// This reads: "while `let` destructures `optional` into
// `Some(i)`, evaluate the block (`{}`). Else `break`.
while let Some(i) = optional {
if i > 9 {
println!("Greater than 9, quit!");
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
// ^ Less rightward drift and doesn't require
// explicitly handling the failing case.
}
Traits
Δημιουργία μιας νέας μεθόδου για έναν τύπο
trait AppendBar {
fn append_bar(self) -> Self;
}
impl AppendBar for String {
fn append_bar(self) -> Self{
format!("{}Bar", self)
}
}
let s = String::from("Foo");
let s = s.append_bar();
println!("s: {}", s);
Δοκιμές
#[cfg(test)]
mod tests {
#[test]
fn you_can_assert() {
assert!(true);
assert_eq!(true, true);
assert_ne!(true, false);
}
}
Πολλαπλές Νήματα
Arc
Ένα Arc μπορεί να χρησιμοποιήσει την Clone για να δημιουργήσει περισσότερες αναφορές πάνω στο αντικείμενο για να τις περάσει στα νήματα. Όταν η τελευταία αναφορά προς μια τιμή είναι εκτός εμβέλειας, η μεταβλητή απορρίπτεται.
use std::sync::Arc;
let apple = Arc::new("the same apple");
for _ in 0..10 {
let apple = Arc::clone(&apple);
thread::spawn(move || {
println!("{:?}", apple);
});
}
Νήματα
Σε αυτήν την περίπτωση θα περάσουμε στο νήμα μια μεταβλητή που θα μπορεί να τροποποιήσει.
fn main() {
let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
let status_shared = Arc::clone(&status);
thread::spawn(move || {
for _ in 0..10 {
thread::sleep(Duration::from_millis(250));
let mut status = status_shared.lock().unwrap();
status.jobs_completed += 1;
}
});
while status.lock().unwrap().jobs_completed < 10 {
println!("waiting... ");
thread::sleep(Duration::from_millis(500));
}
}