DedSec Project

rust — συγκεντρωτικός οδηγός — DedSec Project

rust — συγκεντρωτικός οδηγός Rust Συνήθης χρήση: Προγραμματισμός συστημάτων. Προτεινόμενη επέκταση αρχείου: .rs . Εκτέλεση / περιβάλλον: rustc main.rs && ./main. Αρχικό παράδειγμα Πώς…

Rust

Συνήθης χρήση: Προγραμματισμός συστημάτων.

Προτεινόμενη επέκταση αρχείου: .rs.

Εκτέλεση / περιβάλλον: rustc main.rs && ./main.

Αρχικό παράδειγμα

fn greet(name: &str) -> String { format!("Hello, {}!", name) }
fn main() { println!("{}", greet("Ada")); }

Πώς διαβάζεται

Η ιδιοκτησία και ο δανεισμός αποτρέπουν πολλά σφάλματα μνήμης.

Συνηθισμένο λάθος

Η ιδιοκτησία και ο δανεισμός αποτρέπουν πολλά σφάλματα μνήμης. Έλεγξε την έκδοση διερμηνευτή/μεταγλωττιστή και μην εκλαμβάνεις το δείγμα ως έτοιμη εφαρμογή παραγωγής.

Ownership και borrowing στη Rust

Η Rust εμποδίζει πολλά use-after-free στη μεταγλώττιση: μία τιμή έχει έναν owner, το &T είναι κοινός immutable δανεισμός και το &mut T αποκλειστικός mutable δανεισμός.

fn total(values: &[i32]) -> i32 {
    values.iter().sum()
}
fn main() {
    let values = vec![10, 20, 30];
    assert_eq!(total(&values), 60);
    println!("{}", values.len()); // το borrow δεν μεταφέρει ownership
}

Το Result<T,E> και ο τελεστής ? βοηθούν στον χειρισμό λαθών. Το Option<T> εκφράζει απουσία χωρίς null pointer. Απόφυγε unwrap() σε απρόβλεπτο input. Το Cargo παρέχει cargo check, cargo test, cargo clippy όπου έχουν εγκατασταθεί.

Άσκηση: υλοποίησε parse_positive(&str)->Result<u32,String>, έλεγξε 0, αρνητικά, άκυρο κείμενο και θετικά. Εξήγησε move String έναντι borrow &str. Πηγή: https://doc.rust-lang.org/book/.

Rust: ιδιοκτησία, Result και οριακά tests

Οι κανόνες ownership της Rust καθορίζουν ποιος τροποποιεί δεδομένα και πόσο ισχύουν οι αναφορές. Δοκίμασε:

fn nonempty_name(input: &str) -> Result<&str, &'static str> {
    let name = input.trim();
    if name.is_empty() { return Err("empty name"); }
    Ok(name)
}
fn main() {
    assert_eq!(nonempty_name(" Ada ").unwrap(), "Ada");
    assert!(nonempty_name("   ").is_err());
}

Το αποτέλεσμα δανείζεται ένα slice από την είσοδο αντί να δημιουργεί String. Γιατί δεν μπορεί μια συνάρτηση να επιστρέψει ασφαλώς αναφορά σε προσωρινό String που δημιουργήθηκε μέσα της; Το αντικείμενο καταστρέφεται στο return. Δοκίμασε τέτοιο λανθασμένο παράδειγμα και ερμήνευσε το lifetime error αντί να το παρακάμψεις.

Φτιάξε δεύτερη υλοποίηση που επιστρέφει owned String ώστε ο caller να μην εξαρτάται από τη διάρκεια ζωής της εισόδου. Σύγκρινε API contract και ενδεχόμενη allocation. Διαχειρίσου Ok/Err με match χωρίς panic. Έλεγξε κενό, spaces, Unicode και συνηθισμένο όνομα.

Για compile error προσδιόρισε αν αφορά τύπους, move, borrow ή lifetime και μείωσε το δείγμα. Μη χρησιμοποιείς unsafe για να καλύψεις ασάφεια ownership.

Επαλήθευση: τα tests περνούν και εξηγείς γιατί το borrowed slice απαιτεί ζωντανή αρχική είσοδο. Πηγή: https://doc.rust-lang.org/stable/book/ch09-00-error-handling.

Rust — Αναλυτικός οδηγός εξάσκησης

Στόχος

Ένας parser θετικών u16 πρέπει να επιστρέφει Result αντί να τερματίζει με panic. Οι αναφορές δανείζονται δεδομένα και το String κατέχει τη μνήμη του. Το map_err δείχνει γιατί ο τελεστής ? απαιτεί συμβατό τύπο σφάλματος στην επιστροφή.

Εκτέλεση

Αποθήκευσε main.rs. Εκτέλεσε rustc --test main.rs -o tests && ./tests και έπειτα rustc main.rs -o app && ./app. Περιμένεις επιτυχείς δοκιμές και Ok(42).

fn parse_limit(text: &str) -> Result<u16, String> {
    let number: u16 = text.trim().parse()
        .map_err(|_| "not a u16".to_string())?;
    if number == 0 { return Err("must be positive".to_string()); }
    Ok(number)
}
fn main() { println!("{:?}", parse_limit(" 42 ")); }
#[cfg(test)]
mod tests {
    use super::parse_limit;
    #[test]
    fn boundaries() {
        assert_eq!(parse_limit("42").unwrap(), 42);
        for bad in ["0", "-1", "65536", "word"] {
            assert!(parse_limit(bad).is_err());
        }
    }
}

Κατανόηση

Το trim δημιουργεί &str slice από την αρχική είσοδο. Η parse απορρίπτει αρνητικούς και υπερχείλιση u16. Η απόρριψη μηδενός είναι κανόνας της εφαρμογής, όχι του τύπου. Δοκίμασε να επιστρέψεις αναφορά σε String που δημιουργείται τοπικά: ο compiler σωστά την απορρίπτει επειδή καταστρέφεται ο ιδιοκτήτης.

Επέκταση

Πέρασε &input από δικό σου String και χρησιμοποίησε ξανά το input, αποδεικνύοντας ότι ο δανεισμός δεν το μετέφερε. Αντικατάστησε το String error με enum και χειρίσου το μέσω match αντί unwrap σε μη έμπιστη είσοδο. Έλεγξε κενά, 65535, 65536 και άδειο κείμενο.

Διάγνωση

E0382: συχνά χρήση τιμής μετά από move. E0502: αντικρουόμενοι δανεισμοί. Το rustc --version δείχνει toolchain, ενώ cargo test αφορά Cargo project. Μην κρύβεις σφάλματα ownership με unsafe. Επιτυχία: tests χωρίς panic και κατανόηση του slice. https://doc.rust-lang.org/book/

Rust — Παραδείγματα πραγματικού κώδικα

Εκτέλεση των παραδειγμάτων

Τα παραδείγματα είναι μικρά και αυτοτελή. Χρησιμοποίησε τη σχετική εγκατεστημένη γλώσσα. Ενδεικτικά: rustc {file} -o demo && ./demo. Μη συγχέεις τα ονόματα αρχείων με αυτόματη εγκατάσταση. Δούλεψε σε δοκιμαστικό φάκελο.

Rust — Διαδρομή ουσιαστικής εξάσκησης

Μικρό εργαστήριο με Option

Η μέθοδος get σε slice επιστρέφει Option και επιτρέπει διαχείριση απουσίας στοιχείου χωρίς panic. Το ? σε συνάρτηση που επιστρέφει Option προκαλεί πρόωρη επιστροφή None όταν λείπει τιμή. Το checked_add επιστρέφει None αντί να κάνει σιωπηρή αριθμητική υπερχείλιση.

fn first_two(values: &[i32]) -> Option<i32> {
    let first = *values.get(0)?;
    let second = *values.get(1)?;
    first.checked_add(second)
}
fn main() {
    assert_eq!(first_two(&[3, 5]), Some(8));
    assert_eq!(first_two(&[]), None);
    assert_eq!(first_two(&[i32::MAX, 1]), None);
}

Τα τρία assertions πρέπει να περάσουν στο κατάλληλο Rust compiler. Δεν χρειάζεται unsafe, ούτε ανεξέλεγκτη προσπέλαση με indexing. Δοκίμασε και το [9] ώστε να παρατηρήσεις ξεχωριστά το όριο μήκους.

Ο borrowing κανόνας που εμποδίζει dangling pointer

Φτιάξε προσωρινή συνάρτηση που προσπαθεί να επιστρέψει &i32 προς μεταβλητή που ζει μόνο μέσα στο body της. Ο compiler πρέπει να απορρίψει την αναφορά, επειδή ο δανειζόμενος θα έμενε χωρίς ενεργό owner. Η διόρθωση είναι να επιστρέψεις owned value ή να δανειστείς δεδομένα που ο caller έχει και διατηρεί.

Διαχωρισμός ownership και runtime logic

Η ύπαρξη ελέγχου δανεισμού δεν αποδεικνύει ότι το επιχειρησιακό αποτέλεσμα είναι σωστό. Ακόμη και code με μηδενικά unsafe blocks μπορεί να έχει λάθος μονάδες, μη έγκυρη λογική πολιτικής ή ανεπαρκή έλεγχο JSON. Γράψε unit test που αποτυγχάνει για μία λανθασμένη επιχειρησιακή υπόθεση χωρίς καμία παραβίαση μνήμης.

Παραδοτέο

Τρέξε cargo test με τρεις οριακές περιπτώσεις: κενή λίστα, overflow και κανονικό αποτέλεσμα. Κατάγραψε rustc και cargo versions, τη διάγνωση για το εσκεμμένο dangling reference και τη διαφορά του checked_add από μια απλή πρόσθεση σε debug/release build. Μην ισχυριστείς ότι το παράδειγμα εκτελέστηκε μέσα στο αρχείο γνώσης: τα αποτελέσματα που περιγράφονται είναι προβλέψεις προς επαλήθευση.

Rust — Τεχνικά σημεία και συχνές παγίδες

Τεχνική γνώση

  1. Η ιδιοκτησία και ο δανεισμός περιορίζουν αναφορές και χρόνο ζωής για ασφάλεια μνήμης.
  2. Το Result δηλώνει αποτυχία, το Option απουσία τιμής.
  3. Τα unsafe blocks απαιτούν ειδική τεκμηρίωση και δεν καταργούν όλους τους κανόνες ορθότητας.