Parmi les nouveaux langages de programmation qui gagnent en popularité figure Rust. Rust a été introduit pour la première fois en 2010 et a discrètement gagné en notoriété pour ses performances, sa syntaxe et ses fonctionnalités de sécurité des threads. Si vous êtes un développeur Java, vous trouverez Rust relativement facile à maîtriser, grâce à la similitude des deux langages.
Rust a gravi les échelons de la popularité des langues, ou des langues les plus couramment utilisées, mais le plus révélateur est que Rust continue d’être la “langue la plus appréciée” de toutes, selon l’enquête Stack Overflow. Cela témoigne de la grande expérience d’utilisation de Rust.
Poursuivez votre lecture pour découvrir certaines des principales choses à savoir sur Rust si vous venez d’un milieu Java.
Syntaxe de rouille
Comme Java, Rust est compilé. Il est compilé selon la spécification LLVM, similaire dans l’esprit à la JVM, permettant une sortie vers une variété de plates-formes cibles.
Et comme Java, Rust descend de la lignée C. Son utilisation d’accolades pour les blocs et de points-virgules pour les terminaisons de ligne est exactement la même que celle de Java. Par exemple, vous pouvez voir un programme simple ici, comme Listing 1.
Liste 1. Code Rust simple
fn main() {
println!("Hello, InfoWorld!");
}
Remarquez qu’il y a un main()
fonction, similaire au point d’entrée en Java.
Fonctions dans Rust
Les fonctions sont autonomes dans Rust et peuvent être déclarées n’importe où, y compris imbriquées dans d’autres fonctions. Ceci est différent de Java, où les fonctions sont toujours déclarées comme des méthodes sur des objets (sauf dans le cas des lambdas). Autrement dit, en Java, tout est un objet. Ce n’est pas le cas à Rust.
Listing 2. Utilisation des fonctions dans Rust
fn main() {
println!("Hello, world!");fn function2(){
println!("Hello InfoWorld");
}
function2();function3();
}fn function3() {
println!("Hello again.");
}
Valeurs de retour implicites
Contrairement à Java, Rust vous permet d’ignorer le mot-clé return à la fin d’une fonction. L’instruction finale de la fonction sera automatiquement évaluée comme valeur de retour. Ce faisant, vous omettez le point-virgule de l’instruction finale.
Lambda
Comme Java, Rust prend en charge les lambdas pour le codage de style fonctionnel. La syntaxe est différente, mais ce n’est pas difficile à comprendre si vous êtes familier avec l’API Java streams. Le Listing 3 montre l’utilisation de map()
fonction pour mettre un ensemble de chaînes en majuscules. Comme vous pouvez le voir, c’est assez similaire à Java.
Listing 3. Tri fonctionnel avec lambdas
// Rust
fn main() {
let animals = ["dog", "badger", "quokka"];let result = animals.iter().map(|value| value.to_uppercase());
for animal in result {
println!("Uppercased: {}", animal);
}
}
La map()
La fonction prend un argument en deux parties. La première partie est une variable à l’intérieur des caractères pipe, |value|
, qui définira la variable utilisée comme poignée sur chaque élément. La deuxième partie est l’opération à exécuter. Dans ce cas, on appelle to_uppercase()
sur chaque élément du tableau.
Notez que, comme Java, les lambdas de Rust sont des fermetures qui capturent l’état du bloc environnant. En d’autres termes, ils ont accès au contexte variable dans lequel ils s’exécutent.
Les objets sont des structures dans Rust
Jetez un oeil à la liste 4, qui présente le struct
mot-clé. Une structure, qui est l’abréviation de structure, vous permet de définir un conteneur de données, tout comme la partie état d’une classe en Java.
Listing 4. Utiliser une structure Rust
struct Animal {
name: String
}
fn main() {
let dog = Animal{
name: String::from("Shiba")
};
println!("{}", dog.name);
}
Vous définissez les membres de la structure à l’intérieur de l’accolade de la structure. Ces variables sont analogues aux membres publics.
Notez que dans la ligne où vous déclarez le dog
variable, aucun appel à un nouveau mot-clé n’est nécessaire. Rust peut déduire du contexte qu’une nouvelle référence s’impose.
Ensuite, notez que le name
La variable est définie au moment de la création pour être une chaîne avec une valeur. Cela se fait via l’appel de la fonction intégrée String.from
méthode à l’aide de l’opérateur de référence à double-virgule.
Enfin, notez que tout comme Java, Rust utilise l’opérateur point pour accéder au name
terrain sur le dog
exemple: dog.name
.
Méthodes
Vous pouvez ajouter des fonctions aux structures, et ces fonctions se comportent à peu près de la même manière que les méthodes en Java. Par exemple, pour ajouter un speak()
méthode à la Animal
struct montré dans le Listing 4, vous pouvez utiliser la impl
mot-clé comme vu dans la liste 5.
Listing 5. Ajouter une méthode
impl Animal {
fn speak(&self) {
println!("{}", self.name);
}
}
Impl signifie mise en œuvre. Ici, dans le Listing 5, nous implémentons le Animal
structure. Nous définissons une méthode unique, speak
, qui prend un seul argument. Cet argument est la particularité &self
pointeur (l’esperluette dans Rust signifie que l’argument est une référence). Ce pointeur spécial a une sémantique très similaire à la this
mot clé en java. Il fait référence à l’instance d’objet actuellement active.
Appel dog.speak()
affichera le nom de l’objet instancié actuel, qui est "Shiba"
dans cet exemple.
Mutabilité dans Rust
L’une des choses les plus curieuses à propos de Rust, si vous venez d’un milieu Java, est l’immuabilité par défaut des variables. En bref, lorsque vous déclarez une variable dans Rust, elle est par défaut immuable et toute tentative de modification entraînera une erreur.
Pour rendre une variable mutable, le mut
mot-clé doit être ajouté, mais mut
ne peut être ajouté que par une référence à la fois. N’oubliez pas que Rust est très soucieux de garder le code thread-safe. Cela évite également les erreurs de modification simultanées rencontrées en Java.
Le Listing 6 montre comment faire le dog
objet mutable, puis attribuez-lui un nouveau nom.
Listing 6. Une chaîne mutable
let mut dog = Animal{
name: String::from("Shiba")
};
dog.name = String::from("Suki");
println!("{}", dog.name);
La clé ici est la mut
mot-clé ajouté à la déclaration.
Référence de type dans Rust
Dans Rust, vous n’avez pas toujours à dire au compilateur quel type de variable vous déclarez. Cela semblera étrange pour les développeurs venant de Java, où il n’y a aucune possibilité de déduire le type de variable. Par exemple, dans le Listing 7, le compilateur indique correctement que le type est entier.
Listing 7. Exemple d’inférence de type
let number1 = 10;
let number2 = 10;
println!("{}", number1 * number2);
Ombrage et noms de variables
Une autre fonctionnalité de Rust qui peut surprendre un développeur Java est ce qu’on appelle l’observation variable. Essentiellement, au lieu de déclarer une variable comme modifiable, vous pouvez créer un masque dessus avec le même nom.
Il s’agit d’une sorte de mutabilité unique qui crée un nouvel espace pour le même nom de variable. En général, la possibilité de réutiliser le même nom de variable est différente de Java. Le Listing 8 montre un exemple simple d’ombrage variable.
Listing 8. Ombre variable
fn main() {
let x = 5;
let x = x + 1;
println!("The value of x is: {}", x); // outputs 6
}
Le type de tuple dans Rust
Rust prend en charge un type de tuple, qui est une sorte de variable composée qui n’a pas de véritable analogue en Java. Le Listing 9 vous montre un exemple de tuple en action.
Listing 9. Utiliser le type tuple
fn main() {
let myTuple = ("Sum", 10, 5);
let (x, y) = myTuple ;
println!("The {} is: {}", x, y + z);
}
Ici vous pouvez voir le myTuple
La variable est déclarée avec les parenthèses contenant trois valeurs, une chaîne et deux entiers. Ceci est un tuple.
Vous pouvez “déstructurer” le tuple en variables scalaires comme on le voit dans la ligne suivante, où le let
mot-clé est utilisé pour renseigner chaque variable, x
, y
et z
avec les valeurs du tuple.
Vous pouvez également accéder aux membres du tuple par index. Par exemple, tup.0
fait référence au premier champ du tuple (la chaîne "Sum"
).
Traits et génériques dans Rust
Dans Rust, il existe le concept de traits, qui sont similaires aux interfaces fines de Java : ils définissent les propriétés qu’un type partage avec d’autres types. En d’autres termes, les traits font abstraction des fonctionnalités communes à différents types.
Les génériques de Rust fonctionnent de la même manière que ceux de Java, en utilisant une syntaxe de crochet similaire, pour adresser les types de manière générale en fonction de leurs propriétés partagées.
Jetez un œil à la liste 10, qui résume un exemple d’utilisation des traits du manuel Rust.
Listing 10. Utiliser un trait
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}fn main() {
let tweet = Tweet {
username: String::from("dog_post"),
content: String::from("A Shih Tzu is smaller than a Lurcher",
),
reply: false,
retweet: false,
};println!("1 new tweet: {}", tweet.summarize());
}
Ici le trait
mot-clé est utilisé pour définir un Summary
propriété, qui est ensuite implémentée pour chaque type, NewsArticle
et Tweet
en utilisant le impl
mot-clé. Cela ressemble donc beaucoup à une interface en Java, sauf qu’en Java, une interface définit la surface d’une classe entière, au lieu de définir des méthodes au coup par coup.
Un breuvage pas si étrange
Bien qu’il s’agisse d’un bref tour d’horizon de certains des points les plus saillants pour un développeur Java novice en Rust, vous pouvez voir que le langage n’est pas très difficile à aborder. Il est toujours bon de garder l’esprit ouvert sur les nouvelles technologies, et Rust se recommande avec ses notes de satisfaction constantes des développeurs.
Copyright © 2022 IDG Communications, Inc.