English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Struttura Rust

In Rust, sia le strutture (Struct) che i tuple possono combinare insieme vari tipi di dati non necessariamente diversi per formare un'entità, ma ogni membro della struttura e la struttura stessa hanno un nome, quindi non è necessario ricordare gli indici per accedere ai membri. I tuple sono spesso utilizzati per passare valori multipli non definiti, mentre le strutture sono utilizzate per definire strutture dati comuni. Ogni membro della struttura si chiama "campo".

Definizione di struttura

Questa è una definizione di struttura:

struct Site {
    dominio: String,
    nome: String,
    nazione: String,
    trovato: u32
}

Attenzione: se utilizzi spesso C/C++, ricorda che in Rust la dichiarazione struct serve solo per definire, non per dichiarare esempi, non è necessario il simbolo ; alla fine e ogni definizione di campo deve essere separata da una virgola.

Esempio di struttura

Rust è influenzato da JavaScript in molti aspetti, quando si crea un esempio di struttura utilizza la sintassi key: value dell'oggetto JSON per definire:

let w3codebox = Site {
    domain: String::from("it.oldtoolbag.com"),
    name: String::from("w3codebox"),
    nation: String::from("China"),
    trovato: 2013
};

Se non conosci l'oggetto JSON, puoi non preoccuparti di esso, ricorda solo il formato:

Nome della classe della struttura {
    Nome del campo : valore del campo,
    ...
}

Questo vantaggio non solo rende il programma più intuitivo, ma non è necessario inserire i valori dei membri nell'ordine definito.

Se l'esempio di struttura in esempio ha campi con nomi identici a variabili esistenti, è possibile scrivere in modo semplificato:

let domain = String::from("it.oldtoolbag.com");
let name = String::from("w3codebox");
let w3codebox = Site {
    domain, // equivalente a domain : domain,
    name, // equivalente a name : name,
    nation: String::from("China"),
    traffic: 2013
};

C'è una situazione in cui si desidera creare un esempio di struttura in cui la maggior parte delle proprietà deve essere impostata allo stesso modo di un esempio esistente, modificando solo uno o due valori di campo, si può utilizzare la sintassi di aggiornamento della struttura:

let site = Site {
    domain: String::from("it.oldtoolbag.com"),
    name: String::from("w3codebox"),
    ..w3codebox
};

Attenzione: dopo ..w3codebox non deve esserci una virgola. Questa sintassi non permette di copiare invariabilmente un altro esempio di struttura, il che significa che almeno un valore di campo deve essere riassegnato per utilizzare i valori degli altri esempi.

Struttura tuple

C'è un modo più semplice per definire e utilizzare le strutture:Struttura tuple.

La struttura tuple è una struttura che ha la forma di una tupla.

La differenza rispetto agli array è che ha nomi e un formato di tipo fisso. Esiste per gestire quei dati semplici che richiedono la definizione di un tipo (usati frequentemente) ma non sono troppo complessi:

struct Color(u8, u8, u8);
struct Point(f64, f64);
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0);

"颜色" e "点坐标" sono due tipi di dati comuni, ma se si scrive un esempio con un paio di nomi all'interno di parentesi quadre per migliorare la leggibilità, si sacrifica la convenienza. Rust non lascia questo problema. L'uso degli oggetti di struttura tuple è lo stesso degli array, tramite . e gli indici per l'accesso:

fn main() {
    struct Color(u8, u8, u8);
    struct Point(f64, f64);
    let black = Color(0, 0, 0);
    let origin = Point(0.0, 0.0);
    println!("black = ({}, {}, {})", black.0, black.1, black.2);
    println!("origin = ({}, {})", origin.0, origin.1);
}

Risultato dell'esecuzione:

black = (0, 0, 0)
origin = (0, 0)

Ownership delle strutture

La struttura deve avere una buona comprensione dell'ownership dei campi, perché quando la struttura si disalloca, rilascia tutti i campi.

Ecco perché nel caso di esempio di questa sezione si utilizza il tipo String invece di &str.

Questo non significa che non si definiscono campi di tipo riferimento nella struttura, che devono essere implementati attraverso il meccanismo di "vita".

Ma è difficile spiegare il concetto di "vita" (lifecycle) ora, quindi sarà spiegato nei capitoli successivi.

Esprimere la struttura

Nel debug, mostrare completamente un esempio di struttura è molto utile. Ma se lo scriviamo manualmente, sarà molto scomodo. Quindi Rust fornisce un metodo comodo per esprimere interamente una struttura:

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    println!("rect1 è {:?}", rect1);
}

Come nell'ultima riga: è necessario importare la libreria di debug #[derive(Debug)] Dopo di che, è possibile usare il segnaposto {:?} nei macro println e print per esprimere interamente una struttura:

rect1 è Rectangle { width: 30, height: 50 }

Se ci sono molte proprietà, è possibile utilizzare un altro segnaposto {:#?}.

Risultato di output:

rect1 è Rectangle {
    width: 30,
    height: 50
}

Metodi delle strutture

Il metodo (Method) è simile al funzione (Function), ma è utilizzato per operare sugli esempi delle strutture.

Se hai studiato alcune lingue orientate agli oggetti, probabilmente sai che le funzioni generalmente si trovano all'interno della definizione della classe e si usa this per rappresentare l'esempio che si opera.

Il linguaggio Rust non è orientato agli oggetti, questo si può vedere dalla sua innovativa meccanica di proprietà. Tuttavia, è possibile implementare idee preziose orientate agli oggetti in Rust.

Il primo argomento del metodo della struttura deve essere &self, non è necessario dichiarare il tipo, perché self non è uno stile ma una parola chiave.

Calcolare l'area di un rettangolo:

struct Rectangle {
    width: u32,
    height: u32,
}
    
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}
fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    println!("L'area di rect1 è {}", rect1.area());
}

Risultato di output:

L'area di rect1 è 1500

Si prega di notare che non è necessario scrivere self quando si chiamano i metodi delle strutture, è una scelta per facilità d'uso.

Un esempio di istanza con più parametri:

struct Rectangle {
    width: u32,
    height: u32,
}
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
    fn wider(&self, rect: &Rectangle) -> bool {
        self.width > rect.width
    }
}
fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    let rect2 = Rectangle { width: 40, height: 20 };
    println!("{}", rect1.wider(&rect2));
}

Risultato dell'esecuzione:

false

Questo programma calcola se rect1 è più largo di rect2.

Funzione associata della struttura

Il motivo per cui il "metodo della struttura" non si chiama "funzione della struttura" è perché il nome "funzione" è riservato a questo tipo di funzione: non ha il parametro &self nell'impl blocco.

Queste funzioni non dipendono dall'istanza, ma per usarle è necessario dichiarare in quale blocco impl si trovano.

La funzione String::from che usiamo sempre è una "funzione associata".

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
impl Rectangle {
    fn create(width: u32, height: u32) -> Rectangle {
        Rectangle { width, height }
    }
}
fn main() {
    let rect = Rectangle::create(30, 50);
    println!("{:?}", rect);
}

Risultato dell'esecuzione:

Rectangle { width: 30, height: 50 }

Consiglio:Il blocco impl della struttura può essere scritto più volte, il che ha lo stesso effetto della concatenazione del loro contenuto!

Struttura unitaria

Una struttura può essere solo un simbolo senza bisogno di membri:

struct UnitStruct;

Chiamiamo questa struttura senza corpo una struttura unitaria (Unit Struct).