IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Rust par l'exemple


précédentsommairesuivant

18. Outils standards

Bien d'autres types sont fournis par la bibliothèque standard pour supporter des choses telles que :

  • Les fils d'exécution ;
  • Les canaux ;
  • Les opérations sur le système de fichiers.

Ces types vont bien au-de-là de ce que les primitifs fournissent.

Voir aussi

Les primitifs et la bibliothèque standard.

18-1. Les fils d'exécution

Rust fournit un méchanisme de création de fils d'exécution natifs via la fonction spawn. L'argument de cette fonction est une closure transférable.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
use std::thread;

static NTHREADS: i32 = 10;

// Ceci est le thread `main`.
fn main() {
    // On créé un vecteur pour récupérer tous les threads 
    // enfants qui ont été créés.
    let mut children = vec![];

    for i in 0..NTHREADS {
        // On passe à un autre thread.
        children.push(thread::spawn(move || {
            println!("this is thread number {}", i)
        }));
    }

    for child in children {
        // On attend que le thread se termine. Renvoie un résultat.
        let _ = child.join();
    }
}

Ces threads seront programmés par le système d'exploitation.

18-2. Les canaux

Rust fournit les canaux asynchrones pour la communication entre les threads. Les canaux permettent l'envoi d'un flux unidirectionnel d'information entre deux extrémités : l'envoyeur (Sender) et le receveur (Receiver).

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::thread;

static NTHREADS: i32 = 3;

fn main() {
    // Les canaux possèdent deux extrémités: l'envoyeur (`Sender<T>`) et le receveur (`Receiver<T>`),
    // où `T` est le type du message à envoyer.
    // (Le typage est optionnel)
    let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();

    for id in 0..NTHREADS {
        // L'envoyeur peut être copié.
        let thread_tx = tx.clone();

        // Chaque thread va envoyer son identifiant par le biais du canal.
        thread::spawn(move || {
            // Le thread prend l'ownership sur `thread_tx`.
            // Chaque thread va ajouter un message dans le file d'attente 
            // dans le canal.
            thread_tx.send(id).unwrap();

            // L'envoi est une opération non-bloquante, le thread continuera à 
            // s'exécuter après l'envoi de son message.
            println!("thread {} finished", id);
        });
    }

    // Ici, on récupère tous les messages.
    let mut ids = Vec::with_capacity(NTHREADS as usize);
    for _ in 0..NTHREADS {
        // La méthode `recv` choisit une message se trouvant dans le canal et 
        // gêlera le thread courant s'il n'y a aucun message.
        ids.push(rx.recv());
    }

    // Montre l'ordre dans lequel les messages ont été envoyés.
    println!("{:?}", ids);
}

18-3. La structure Path

La structure Path représente les chemins de fichiers dans le système de fichiers sous-jacent. Il y a deux variantes de Path :

  1. posix::Path, pour les systèmes UNIX-like ;
  2. windows::Path, pour Windows.

Le prélude exporte la variante de Path adaptée à la plateforme.

Une instance de Path peut être créée à partir du type OsStr et fournit de nombreuses méthodes pour obtenir des informations à propos du fichier/répertoire sur lequel le chemin pointe.

Notez que, en interne, un objet Path n'est pas représenté par une chaîne de caractères UTF-8 mais est stocké dans un vecteur d'octets (Vec<u8>). En conséquence, la conversion d'un objet Path en &str n'est pas gratuite et peut échouer (un objet Option est renvoyé).

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
use std::path::{Path, PathBuf};
use std::ffi;

fn main() {
    // Création d'un objet `Path` à partir d'une `&'static str`.
    let path = Path::new(".");

    // La méthode `display` renvoie une structure présentable.
    let display = path.display();

    // La méthode `join()` fusionne un chemin avec toutes les ressources 
    // possédant une implémentation de la méthode `as_ref()` pour le type `Path` et 
    // renvoie un nouveau chemin avec le séparateur spécifique à l'OS.
    let new_path = path.join("a").join("b");

    // Convertit le chemin en une vue sur une string.
    match new_path.to_str() {
        None => panic!("new path is not a valid UTF-8 sequence"),
        Some(s) => println!("new path is {}", s),
    }
}

N'hésitez pas à cosnulter les autres méthodes de Path et la structure Metadata.

Voir aussi

OsStr et Metadata.

18-4. La structure File

La structure File représente un fichier qui a été ouvert (contient un descripteur de fichier), et donne les accès lecture et/ou écriture sur le fichier sous-jacent.

Puisque de nombreuses choses peuvent mal se passer lorsqu'une opération est effectuée, toutes les méthodes de File renvoie le type io::Result<T>, lequel étant un alias pour Result<T, io::Error>.

Ceci couvre les potentielles erreurs de toutes les opérations d'entrée/sortie explicites. Grâce à cela, le programmeur peut visualiser toutes les erreurs possibles et est encouragé à les anticiper.

18-4-1. La méthode open

La méthode statique open() peut être utilisé pour ouvrir un fichier en lecture seule.

Un objet File est responsable d'une ressource, du descripteur de fichier et prend soin de fermer le fichier lorsqu'il est libéré.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
// Dans le fichier open.rs
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

fn main() {
    // Crée un chemin vers le fichier désiré.
    let path = Path::new("hello.txt");
    let display = path.display();

    // Ouvre le chemin en lecture seule, renvoie un objet `io::Result<File>`.
    let mut file = match File::open(&path) {
        // La méthode `description` de `io::Error` renvoie une chaîne de caractères
        // qui décrit l'erreur.
        Err(why) => panic!("couldn't open {}: {}", display,
                                                   why.description()),
        Ok(file) => file,
    };

    // Lit le contenu du fichier dans une chaîne de caractères, renvoie un objet `io::Result<usize>`.
    let mut s = String::new();
    match file.read_to_string(&mut s) {
        Err(why) => panic!("couldn't read {}: {}", display,
                                                   why.description()),
        Ok(_) => print!("{} contains:\n{}", display, s),
    }

    // `file` sort du contexte, le flux ouvert sur le fichier "hello.txt" 
    // va être fermé.
}

Voici le résultat attendu :

 
Sélectionnez
1.
2.
3.
4.
$ echo "Hello World!" > hello.txt
$ rustc open.rs && ./open
hello.txt contains:
Hello World!

Nous vous encourageons à confronter l'exemple précédent à des cas d'échec différents (e.g. hello.txt n'existe pas, hello.txt ne peut pas être lu).

18-4-2. La méthode create

La méthode statique create() ouvre un fichier en écriture seule. Si le fichier existe déjà, le contenu sera écrasé. Autrement, un nouveau fichier sera créé.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
// Dans le fichier create.rs
static LOREM_IPSUM: &'static str =
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
";

use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
use std::path::Path;

fn main() {
    let path = Path::new("out/lorem_ipsum.txt");
    let display = path.display();

    // Ouvre un fichier en écriture seule, renvoie un objet `io::Result<File>`.
    let mut file = match File::create(&path) {
        Err(why) => panic!("couldn't create {}: {}",
                           display,
                           why.description()),
        Ok(file) => file,
    };

    // Écrit la chaîne de caractères de `LOREM_IPSUM` dans `file`, renvoie 
    // un objet `io::Result<()>`.
    match file.write_all(LOREM_IPSUM.as_bytes()) {
        Err(why) => {
            panic!("couldn't write to {}: {}", display,
                                               why.description())
        },
        Ok(_) => println!("successfully wrote to {}", display),
    }
}

Voici le résultat attendu :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
$ mkdir out
$ rustc create.rs && ./create
successfully wrote to out/lorem_ipsum.txt
$ cat out/lorem_ipsum.txt
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Comme pour l'exemple précédent, nous vous encourageons à confronter l'exemple à d'autres cas où l'opération pourrait échouer.

Il existe également d'autres outils pouvant ouvrir des fichiers dans des modes différents tels que : read+write, append, etc.

18-5. Les sous-processus

La structure process::Output représente la sortie d'un sous-processus terminé et la structure process::Command est un constructeur de processus.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
use std::process::Command;

fn main() {
    let output = Command::new("rustc")
        .arg("--version")
        .output().unwrap_or_else(|e| {
            panic!("failed to execute process: {}", e)
    });

    if output.status.success() {
        let s = String::from_utf8_lossy(&output.stdout);

        print!("rustc succeeded and stdout was:\n{}", s);
    } else {
        let s = String::from_utf8_lossy(&output.stderr);

        print!("rustc failed and stderr was:\n{}", s);
    }
}

Nous vous encourageons à essayer l'exemple précédent en lançant rustc avec un flag incorrect.

18-5-1. Les pipes

La structure Process représente un processus en cours d'exécution et expose la gestion de stdin, stdout et stderr pour interagir avec le processus sous-jacent via les pipes.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
use std::error::Error;
use std::io::prelude::*;
use std::process::{Command, Stdio};

static PANGRAM: &'static str =
"the quick brown fox jumped over the lazy dog\n";

fn main() {
    // On crée un processus dans lequel la commande `wc` va s'exécuter.
    let process = match Command::new("wc")
                                .stdin(Stdio::piped())
                                .stdout(Stdio::piped())
                                .spawn() {
        Err(why) => panic!("couldn't spawn wc: {}", why.description()),
        Ok(process) => process,
    };

    // On écrit quelque chose dans l'entrée standard de `wc`.
    // `stdin` est de type `Option<ChildStdin>`, mais puisque nous savons que 
    // cette instance en possède une, nous pouvons directement l'`unwrap()`.
    match process.stdin.unwrap().write_all(PANGRAM.as_bytes()) {
        Err(why) => panic!("couldn't write to wc stdin: {}",
                           why.description()),
        Ok(_) => println!("sent pangram to wc"),
    }

    // Puisque `stdin` ne survit pas après l'appel du dessus, elle va être libérée et 
    // et le pipe fermé.
    //
    // C'est très important sinon `wc` ne pourrait pas commencer à traiter l'entrée 
    // que nous avons soumis.

    // La champ `stdout` est également de type `Option<ChildStdout>` et doit donc être `unwrap()`.
    let mut s = String::new();
    match process.stdout.unwrap().read_to_string(&mut s) {
        Err(why) => panic!("couldn't read wc stdout: {}",
                           why.description()),
        Ok(_) => print!("wc responded with:\n{}", s),
    }
}

18-5-2. La méthode wait

Vous souhaiteriez peut-être attendre qu'un processus, dont un objet process::Child est responsable, se termine. Pour cela vous devez appeler la méthode Child::wait qui renverra un objet process::ExitStatus.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
// Dans le fichier wait.rs
use std::process::Command;

fn main() {
    let mut child = Command::new("sleep").arg("5").spawn().unwrap();
    let _result = child.wait().unwrap();

    println!("reached end of main");
}
 
Sélectionnez
1.
2.
3.
4.
$ rustc wait.rs && ./wait
reached end of main
# `wait` s'est exécuté pendant 5 secondes.
# Une fois la commande `sleep 5` terminée notre programme `wait` a pris fin.

18-6. Opérations sur le système de fichiers

Le module std::io::fs contient de nombreuses fonctions traitant avec le système de fichiers.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
// Dans le fichier fs.rs
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
use std::os::unix;
use std::path::Path;

// Une simple implémentation de la commande `cat path`.
fn cat(path: &Path) -> io::Result<String> {
    let mut f = try!(File::open(path));
    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

// Une simple implémentation de la commande `echo s > path`.
fn echo(s: &str, path: &Path) -> io::Result<()> {
    let mut f = try!(File::create(path));

    f.write_all(s.as_bytes())
}

// Une simple implémentation de la commande `touch path` (ignore les fichiers existants).
fn touch(path: &Path) -> io::Result<()> {
    match OpenOptions::new().create(true).write(true).open(path) {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}

fn main() {
    println!("`mkdir a`");
    // Crée un répertoire et renvoie un objet `io::Result<()>`.
    match fs::create_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(_) => {},
    }

    println!("`echo hello > a/b.txt`");
    // Le match précédent peut être simplifié en utilisant la méthode `unwrap_or_else()`.
    echo("hello", &Path::new("a/b.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`mkdir -p a/c/d`");
    // Crée un répertoire récursivement, renvoie un objet `ìo::Result<()>`.
    fs::create_dir_all("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`touch a/c/e.txt`");
    touch(&Path::new("a/c/e.txt")).unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`ln -s ../b.txt a/c/b.txt`");
    // Crée un lien symbolique, renvoie un objet `io::Result<()>`.
    if cfg!(target_family = "unix") {
        unix::fs::symlink("../b.txt", "a/c/b.txt").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
        });
    }

    println!("`cat a/c/b.txt`");
    match cat(&Path::new("a/c/b.txt")) {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(s) => println!("> {}", s),
    }

    println!("`ls a`");
    // Lit le contenu d'un répertoire, renvoie un objet `io::Result<Vec<Path>>`.
    match fs::read_dir("a") {
        Err(why) => println!("! {:?}", why.kind()),
        Ok(paths) => for path in paths {
            println!("> {:?}", path.unwrap().path());
        },
    }

    println!("`rm a/c/e.txt`");
    // Supprime un fichier, renvoie un objet `io::Result<()>`.
    fs::remove_file("a/c/e.txt").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });

    println!("`rmdir a/c/d`");
    // Supprime un répertoire vide, renvoie un objet `io::Result<()>`.
    fs::remove_dir("a/c/d").unwrap_or_else(|why| {
        println!("! {:?}", why.kind());
    });
}

Voici le résultat attendu :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
$ rustc fs.rs && ./fs
`mkdir a`
`echo hello > a/b.txt`
`mkdir -p a/c/d`
`touch a/c/e.txt`
`ln -s ../b.txt a/c/b.txt`
`cat a/c/b.txt`
> hello
`ls a`
> "a/b.txt"
> "a/c"
`rm a/c/e.txt`
`rmdir a/c/d`

Et l'état final du répertoire a est :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
$ tree a
a
|-- b.txt
`-- c
    `-- b.txt -> ../b.txt

1 directory, 2 files

Voir aussi

La macro cfg!.

18-7. Les arguments du programme

Les arguments passés en ligne de commande peuvent être récupérés en utilisant std::env::args qui renvoie un itérateur fournissant une String pour chaque argument :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    // Le premier argument est le chemin à partir duquel le programme 
    // a été appelé.
    println!("My path is {}.", args[0]);

    // Le reste des arguments sont ceux passés en ligne de commande au programme.
    // On appelle le programme comme ceci:
    //  $ ./args arg1 arg2
    println!("I got {:?} arguments: {:?}.", args.len() - 1, &args[1..]);
}
 
Sélectionnez
$ ./args 1 2 3
My path is ./args.
I got 3 arguments: ["1", "2", "3"].

18-7-1. Récupération des arguments

Le pattern matching peut être utilisé pour traiter de simples arguments :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
use std::env;

fn increase(number: i32) {
    println!("{}", number + 1);
}

fn decrease(number: i32) {
    println!("{}", number - 1);
}

fn help() {
    println!("usage:
match_args <string>
    Check whether given string is the answer.
match_args {{increase|decrease}} <integer>
    Increase or decrease given integer by one.");
}

fn main() {
    let args: Vec<String> = env::args().collect();

    match args.len() {
        // Pas d'arguments passés.
        1 => {
            println!("My name is 'match_args'. Try passing some arguments!");
        },
        // Un seul argument passé.
        2 => {
            match args[1].parse() {
                Ok(42) => println!("This is the answer!"),
                _ => println!("This is not the answer."),
            }
        },
        // Une commande et un argument passé.
        3 => {
            let cmd = &args[1];
            let num = &args[2];
            // On traite le nombre.
            let number: i32 = match num.parse() {
                Ok(n) => {
                    n
                },
                Err(_) => {
                    println!("error: second argument not an integer");
                    help();
                    return;
                },
            };
            // On traite la commande.
            match &cmd[..] {
                "increase" => increase(number),
                "decrease" => decrease(number),
                _ => {
                    println!("error: invalid command");
                    help();
                },
            }
        },
        // On couvre tous les autres cas...
        _ => {
            // ... en affichant l'aide.
            help();
        }
    }
}
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
$ ./match_args Rust
This is not the answer.
$ ./match_args 42
This is the answer!
$ ./match_args do something
error: second argument not an integer
usage:
match_args <string>
    Check whether given string is the answer.
match_args {increase|decrease} <integer>
    Increase or decrease given integer by one.
$ ./match_args do 42
error: invalid command
usage:
match_args <string>
    Check whether given string is the answer.
match_args {increase|decrease} <integer>
    Increase or decrease given integer by one.
$ ./match_args increase 42
43

18-8. FFI

Rust fournit une Interface pour Fonction Externe (« Foreign Function Interface », dans la langue de Shakespear) pour les bibliothèques écrites en C. Les fonctions externes peuvent être déclarées dans un bloc extern annoté de l'attribut #[link] contenant le nom de la bibliothèque externe.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
// Dans le fichier ffi.rs
use std::fmt;

// Ce bloc externe lie la bibliothèque libm.
#[link(name = "m")]
extern {
    // Ceci est une fonction externe 
    // qui calcule la racine carrée d'un nombre complexe à précision simple.
    fn csqrtf(z: Complex) -> Complex;
}

fn main() {
    // z = -1 + 0i
    let z = Complex { re: -1., im: 0. };

    // Appeler une fonction externe est une opération dite "à risque".
    let z_sqrt = unsafe {
        csqrtf(z)
    };

    println!("the square root of {:?} is {:?}", z, z_sqrt);
}

// Implémentation minimale d'un nombre complex à précision simple.
#[repr(C)]
#[derive(Clone, Copy)]
struct Complex {
    re: f32,
    im: f32,
}

impl fmt::Debug for Complex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.im < 0. {
            write!(f, "{}-{}i", self.re, -self.im)
        } else {
            write!(f, "{}+{}i", self.re, self.im)
        }
    }
}
 
Sélectionnez
$ rustc ffi.rs && ./ffi
the square root of -1+0i is 0+1i

Puisque l'appel de fonctions externes est considéré comme « à risque », il est courant d'écrire des wrappers sécurisés.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
// Dans le fichier safe.rs
use std::fmt;

#[link(name = "m")]
extern {
    fn ccosf(z: Complex) -> Complex;
}

// Wrapper sécurisé.
fn cos(z: Complex) -> Complex {
    unsafe { ccosf(z) }
}

fn main() {
    // z = 0 + 1i
    let z = Complex { re: 0., im: 1. };

    println!("cos({:?}) = {:?}", z, cos(z));
}

// Implémentation minimale d'un nombre complexe à précision simple.
#[repr(C)]
#[derive(Clone, Copy)]
struct Complex {
    re: f32,
    im: f32,
}

impl fmt::Debug for Complex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.im < 0. {
            write!(f, "{}-{}i", self.re, -self.im)
        } else {
            write!(f, "{}+{}i", self.re, self.im)
        }
    }
}

précédentsommairesuivant

Licence Creative Commons
Le contenu de cet article est rédigé par Rust Core Team et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.