Share this page

Learn X in Y minutes

Where X=Rust

[ΣτΜ.: οι όροι “χαμηλό/υψηλό επίπεδο” αναφέρονται στην εγγύτητα μιας γλώσσας προγραμματισμού ή γενικότερα ενός στοιχείου στην “μηχανή”, ή το υλικό του υπολογιστή. Για παράδειγμα, η φράση “η C είναι μια γλώσσα χαμηλού επιπέδου” αναφέρεται στο γεγονός ότι η C επιτρέπει άμεση και λεπτομερή διαχείρηση μνήμης, και πιο άμεσο έλεγχο του επεξεργαστή· σε καμία περίπτωση δεν σημαίνει ότι η C έχει λιγότερες δυνατότητες, και γενικότερα δεν φέρει αρνητική σημασία.]

Η Rust είναι μια γλώσσα προγραμματισμού ανεπτυγμένη από την Mozilla Research. Συνδυάζει τον έλεγχο της απόδοσης χαμηλού επιπέδου με διευκολύνσεις και ασφάλεια υψηλού επιπέδου.

Πετυχαίνει αυτούς τους στόχους χωρίς να χρειάζεται garbage collector ή runtime, το οποίο καθιστά δυνατή τη χρήση βιβλιοθηκών της Rust ως αντικατάσταση της C.

Η έκδοση 0.1 (η πρώτη της Rust) δημοσιεύθηκε τον Ιανουάριο του 2012, και για τα επόμενα 3 χρόνια η ανάπτυξή της εξελίχθηκε τόσο γρήγορα που, μέχρι πρότινος, προτείνονταν η χρήση μη-σταθερών εκδόσεων (nightly builds) αντί σταθερών εκδόσεων.

Τις 15 Μαΐου 2015 δημοσιεύτηκε η εκδοχή 1.0 της Rust, με πλήρη εγγύηση συμβατότητας με προηγούμενες εκδοχές. Οι μη-σταθερές εκδόσεις συνήθως περιλαμβάνουν γρηγορότερους χρόνους μεταγλώττισης και γενικότερες βελτιώσεις όσον αφορά τον μεταγλωττιστή. Η μέθοδος train release χρησιμοποιείται, με συστηματικές εκδόσεις να δημοσιεύονται κάθε έξι εβδομάδες. Η beta έκδοση της Rust 1.1 δημοσιεύθηκε ταυτοχρόνως με την σταθερή έκδοση 1.0.

Αν και η Rust είναι μια γλώσσα σχετικά χαμηλού επιπέδου, ο σχεδιασμός της περιλαμβάνει κάποιες έννοιες που συναντώνται συνχότερα σε γλώσσες υψηλού επιπέδου. Αυτό καθιστά την Rust γρήγορη και αποδοτική αλλά επίσης εύκολη και προσβάσιμη.

// Αυτό είναι ένα σχόλιο. Τα σχόλια μίας γραμμής γράφονται έτσι...
// Και επεκτείνονται σε περισσότερες από μία γραμμές έτσι.

/// Τα σχόλια documentation γράφονται έτσι, και υποστηρίζουν markdown.
/// # Παράδειγμα
/// 
/// ```
/// let five = 5
/// ```

//////////////////////
// 1. Βασικές αρχές //
//////////////////////

#[allow(dead_code)]
// Συναρτήσεις
// `i32` είναι ο τύπος που αντιστοιχεί στους 32-bit signed ακέραιους
fn add2(x: i32, y: i32) -> i32 {
    // Έμεσα εννοούμενη επιστροφή του αποτελέσματος, χωρίς semicolon (;)
    x + y
}

#[allow(unused_variables)]
#[allow(unused_assignments)]
#[allow(dead_code)]
// Συνάρτηση main
fn main() {
    // Αριθμοί //

    // Αμετάβλητη σύνδεση
    let x: i32 = 1;

    // Καταλήξεις integer/float
    let y: i32 = 13i32;
    let f: f64 = 1.3f64;

    // Εξακρίβωση τύπου (type inference)
    // Τις περισσότερες φορες ο μεταγλωττιστής της Rust μπορεί να εξακριβώσει τον τύπο μιας μεταβλητής, επομένως δεν
    // χρειάζεται ο προγραμματιστής να τον δηλώνει ρητά.
    // Σε αυτό το tutorial, οι τύποι δηλώνονται ρητά σε διάφορα σημεία, αλλά μόνο προκειμένου να είναι πιο ευανάγνωστος
    // ο κώδικας. Ο μεταγλωττιστής μπορεί να το διαχειριστεί αυτόματα στις περισσότερες περιπτώσεις.
    let implicit_x = 1;
    let implicit_f = 1.3;

    // Πράξεις
    let sum = x + y + 13;

    // Μη-αμετάβλητη αξία (με την έννοια ότι μπορεί να αλλάξει)
    let mut mutable = 1;
    mutable = 4;
    mutable += 2;

    // Αλφαριθμητικά //

    // Σταθερά αλφαριθμητικά
    let x: &str = "καλημέρα κόσμε!";

    // Εκτύπωση αλφαριθμητικών
    println!("{} {}", f, x); // 1.3 καλημέρα κόσμε!

    // A `String` – a heap-allocated string
    let s: String = "καλημέρα κόσμε".to_string();

    // Ένα κομμάτι αλφαριθμητικού (string slice) – μια μη-μεταβλητή οπτική γωνία προς ένα άλλο αλφαριθμητικό
    // Το αλφαριθμητικό μπορεί να είναι στατικό όπως τα σταθερά αλφαριθμητικά, ή να περιλαμβάνεται σε ένα άλλο,
    // δυναμικό αντικείμενο (σε αυτή την περίπτωση τη μεταβλητή `s`)
    let s_slice: &str = &s;

    println!("{} {}", s, s_slice); // καλημέρα κόσμε καλημέρα κόσμε

    // Διανύσματα/πίνακες //

    // Πίνακας σταθερού μεγέθους
    let four_ints: [i32; 4] = [1, 2, 3, 4];

    // Δυναμικός πίνακας (διάνυσμα)
    let mut vector: Vec<i32> = vec![1, 2, 3, 4];
    vector.push(5);

    // Ένα κομμάτι – μια μη-μεταβλητή οπτική γωνία προς ένα διάνυσμα ή πίνακα
    // Είναι παρόμοιο με το κομμάτι αλφαριθμητικού που είδαμε προηγουμένως
    let slice: &[i32] = &vector;

    // Μπορούμε να χρησιμοποιήσουμε το `{:?}` για να εκτυπώσουμε κάτι σε στυλ debug
    println!("{:?} {:?}", vector, slice); // [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]

    // Tuples (πλειάδες) //

    // Ένα tuple είναι μια σταθερού μεγέθους σειρά από αξίες (πιθανά διαφορετικού τύπου)
    let x: (i32, &str, f64) = (1, "καλημέρα", 3.4);

    // Μπορούμε να χρησιμοποιήσουμε το `let` και ένα tuple για να δώσουμε πολλές αξίες σε πολλές μεταβλητές ταυτόχρονα
    // (destructuring `let`)
    let (a, b, c) = x;
    println!("{} {} {}", a, b, c); // 1 καλημέρα 3.4

    // Μπορούμε επίσης να επιλέξουμε ένα συγκεκριμένο στοιχείο από ένα tuple
    println!("{}", x.1); // καλημέρα

    //////////////
    // 2. Τύποι //
    //////////////

    // Δομή
    struct Point {
        x: i32,
        y: i32,
    }

    let origin: Point = Point { x: 0, y: 0 };

    // Μια δομή με ανώνυμα πεδία, ή αλλιώς μια `δομή tuple` (`tuple struct`)
    struct Point2(i32, i32);

    let origin2 = Point2(0, 0);

    // Enum, όπως στην C
    enum Direction {
        Left,
        Right,
        Up,
        Down,
    }

    let up = Direction::Up;

    // Enum με πεδία
    enum OptionalI32 {
        AnI32(i32),
        Nothing,
    }

    let two: OptionalI32 = OptionalI32::AnI32(2);
    let nothing = OptionalI32::Nothing;

    // Γενικότητα (genericity) //

    struct Foo<T> { bar: T }

    // Αυτό ορίζεται στην standard library ως `Option`
    enum Optional<T> {
        SomeVal(T),
        NoVal,
    }

    // Μέθοδοι //

    impl<T> Foo<T> {
        // Οι μέθοδοι παίρνουν πάντα μια ρητή παράμετρο `self`
        fn bar(&self) -> &T { // Δανειζόμαστε το self
            &self.bar
        }
        fn bar_mut(&mut self) -> &mut T { // Δανειζόμαστε το self ως μη-αμετάβλητη αξία
            &mut self.bar
        }
        fn into_bar(self) -> T { // Εδώ το self καταναλώνεται
            self.bar
        }
    }

    let a_foo = Foo { bar: 1 };
    println!("{}", a_foo.bar()); // 1

    // Χαρακτηρηστικά (traits) (γνωστά ως interfaces ή typeclasses σε άλλες γλώσσες) //

    trait Frobnicate<T> {
        fn frobnicate(self) -> Option<T>;
    }

    impl<T> Frobnicate<T> for Foo<T> {
        fn frobnicate(self) -> Option<T> {
            Some(self.bar)
        }
    }

    let another_foo = Foo { bar: 1 };
    println!("{:?}", another_foo.frobnicate()); // Some(1)

    //////////////////////////////////////////////////
    // 3. Αντιστοιχίσεις Μοτίβων (Pattern Matching) //
    //////////////////////////////////////////////////

    let foo = OptionalI32::AnI32(1);
    match foo {
        OptionalI32::AnI32(n) => println!("Είναι ένα i32: {}", n),
        OptionalI32::Nothing  => println!("Δεν είναι τίποτα!"),
    }

    // Προχωρημένο pattern matching
    struct FooBar { x: i32, y: OptionalI32 }
    let bar = FooBar { x: 15, y: OptionalI32::AnI32(32) };

    match bar {
        FooBar { x: 0, y: OptionalI32::AnI32(0) } =>
            println!("Οι αριθμοί είναι μηδέν!"),
        FooBar { x: n, y: OptionalI32::AnI32(m) } if n == m =>
            println!("Οι αριθμοί είναι οι ίδιοι"),
        FooBar { x: n, y: OptionalI32::AnI32(m) } =>
            println!("Διαφορετικοί αριθμοί: {} {}", n, m),
        FooBar { x: _, y: OptionalI32::Nothing } =>
            println!("Ο δεύτερος αριθμός δεν είναι τίποτα!"),
    }

    /////////////////////
    // 4. Έλεγχος ροής //
    /////////////////////

    // Βρόγχοι `for`
    let array = [1, 2, 3];
    for i in array.iter() {
        println!("{}", i);
    }

    // Διαστήματα
    for i in 0u32..10 {
        print!("{} ", i);
    }
    println!("");
    // Τυπώνει `0 1 2 3 4 5 6 7 8 9 `

    // Βρόγχοι `if`
    if 1 == 1 {
        println!("Τα μαθηματικά δουλεύουν!");
    } else {
        println!("Ωχ όχι...");
    }

    // `if` ως έκφραση
    let value = if true {
        "καλό"
    } else {
        "κακό"
    };

    // Βρόγχοι `while`
    while 1 == 1 {
        println!("Το σύμπαν λειτουργεί κανονικά.");
        // Μπορούμε να βγούμε από το βρόγχο με το `break`
        break
    }

    // Ατέρμονος βρόχγος
    loop {
        println!("Καλημέρα!");
        // Μπορούμε να βγούμε από το βρόγχο με το `break`
        break
    }

    //////////////////////////////////
    // 5. Ασφάλεια μνήμης & δείκτες //
    //////////////////////////////////

    // Δείκτης με ιδιοκτήτη – μόνο ένα αντικείμενο μπορεί να είναι ο "ιδιοκτήτης" αυτού του δείκτη ανά πάσα στιγμή
    // Αυτό σημαίνει ότι μόλις το `Box` βγει εκτός πλαισίου (out of scope), ο δείκτης μπορεί να ελευθερωθεί με ασφάλεια
    let mut mine: Box<i32> = Box::new(3);
    *mine = 5; // Dereference του δείκτη
    // Εδώ, το `now_its_mine` γίνεται ιδιοκτήτης του `mine`. Δηλαδή, το `mine` μετακινείται.
    let mut now_its_mine = mine;
    *now_its_mine += 2;

    println!("{}", now_its_mine); // 7
    // println!("{}", mine); // Αυτό παράγει λάθος κατά τη μεταγλώττιση διότι τώρα ο δείκτης ανοίκει στο `now_its_mine`

    // Reference (αναφορά) – ένας αμετάβλητος δείκτης που αναφέρεται σε άλλα δεδομένα
    // Όταν μια αναφορά δίνεται σε μια αξία, λέμε πως η αξία έχει "δανειστεί".
    // Όταν μια αξία δανείζεται αμετάβλητα, δεν μπορεί να είναι mutated (να μεταβληθεί) ή να μετακινηθεί.
    // Ένας "δανεισμός" παραμένει ενεργός μέχρι την τελευταία χρήση της μεταβλητής που δανείζεται.
    let mut var = 4;
    var = 3;
    let ref_var: &i32 = &var;

    println!("{}", var); // Αντίθετα με το `mine` προηγουμένως, η μεταβλητή `var` μπορεί ακόμα να χρησιμοποιηθεί
    println!("{}", *ref_var);
    // var = 5; // Αυτό παράγει λάθος κατά τη μεταγλώττιση γιατί η μεταβλητή `var` είναι δανεισμένη
    // *ref_var = 6; // Το ίδιο εδώ, γιατί η `ref_var` αποτελεί αμετάβλητη αναφορά
    ref_var; // Εντολή no-op (τίποτα δεν εκτελείται από τον επεξεργαστή), η οποία όμως μετράει ως χρήση και κρατά τον
             // "δανεισμό" ενεργό
    var = 2; // Η `ref_var` δεν χρησιμοποιείται από εδώ και στο εξής, άρα ο "δανεισμός" τελειώνει

    // Μεταβλητή αναφορά
    // Όσο μια αξία είναι μεταβλητά δανεισμένη, παραμένει τελείως απροσβάσιμη.
    let mut var2 = 4;
    let ref_var2: &mut i32 = &mut var2;
    *ref_var2 += 2; // Ο αστερίσκος (*) χρησιμοποιείται ως δείκτης προς την μεταβλητά δανεισμένη `var2`

    println!("{}", *ref_var2); // 6 , // Αν είχαμε `var2` εδώ θα προκαλούνταν λάθος μεταγλώττισης.
    // O τύπος της `ref_var2` είναι &mut i32, άρα αποθηκεύει μια αναφορά προς μια αξία i32, όχι την αξία την ίδια.
    // var2 = 2; // Λάθος μεταγλώττισης, γιατί η `var2` είναι δανεισμένη.
    ref_var2; // Εντολή no-op (τίποτα δεν εκτελείται από τον επεξεργαστή), η οποία όμως μετράει ως χρήση και κρατά τον
              // "δανεισμό" ενεργό
}

Μάθετε περισσότερα

Υπάρχουν πολλά ακόμα πράγματα να μάθει κανείς· αυτά είναι μόνο τα βασικά της Rust, που επιτρέπουν να καταλάβουμε το βασικό τρόπο λειτουργίας της. Για να μάθετε περισσότερα για τη Rust, διαβάστε το The Rust Programming Language και επισκεφθείτε το subreddit /r/rust. Οι άνθρωποι πίσω από το κανάλι #rust στο irc.mozilla.org είναι επίσης πάντα πρόθυμοι να βοηθήσουν τους αρχάριους.

Μπορείτε επίσης να παίξετε με τη Rust χρησιμοποιώντας τους εξής online μεταγλωττιστές:


Got a suggestion? A correction, perhaps? Open an Issue on the Github Repo, or make a pull request yourself!

Originally contributed by P1start, and updated by 0 contributor(s).