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

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

La version 1.92 du langage de programmation Rust est disponible, introduisant les tables de déroulement par défaut et affinant les vérifications de lints et d'attributs

Le , par Alex

39PARTAGES

5  0 
La version 1.92 du langage de programmation Rust est disponible, introduisant les tables de déroulement par défaut et affinant les vérifications de lints et d'attributs

Rust 1.92 est la dernière version du langage de programmation polyvalent, qui introduit des changements importants pour les développeurs. Notamment, les tables de déroulement sont désormais émises par défaut même lorsque le drapeau -Cpanic=abort est activé, ce qui garantit le bon fonctionnement des traces de retour en arrière dans les scénarios de panique-abandon. Les utilisateurs qui ne souhaitent pas utiliser les tables de déroulement peuvent désormais les désactiver explicitement avec -Cforce-unwind-tables=no.

Rust est un langage de programmation compilé multi-paradigme qui met l'accent sur la performance, la sûreté des types et la concurrence. Il assure la sécurité mémoire, ce qui signifie que toutes les références pointent vers une mémoire valide, sans nécessiter l'utilisation de techniques de gestion de la mémoire automatisée telles que le ramasse-miettes. Afin d'assurer la sécurité de la mémoire et d'empêcher une situation de compétition aux données, son « vérificateur d'emprunts » suit la durée de vie des objets de toutes les références dans un programme à la compilation. Rust a été remarqué pour son adoption rapide, selon le Stack Overflow Survey 2025, c'est le langage le plus apprécié dans ce sondage.

Rust 1.92 est la dernière version du langage de programmation polyvalent, qui introduit des changements importants pour les développeurs. Notamment, les tables de déroulement sont désormais émises par défaut même lorsque le drapeau -Cpanic=abort est activé, ce qui garantit le bon fonctionnement des traces de retour en arrière dans les scénarios de panique-abandon. Les utilisateurs qui ne souhaitent pas utiliser les tables de déroulement peuvent désormais les désactiver explicitement avec -Cforce-unwind-tables=no.

Suite aux améliorations continues apportées au système de types, cette version poursuit les efforts de stabilisation du type never. Deux futurs lints de compatibilité, never_type_fallback_flowing_into_unsafe et dependency_on_unit_never_type_fallback, sont désormais refusés par défaut, ce qui signifie que toute violation entraînera des erreurs de compilation plutôt que des avertissements ou une autorisation silencieuse.

En ce qui concerne les lints, le lint unused_must_use n'émet plus d'avertissement lorsque les fonctions renvoient un Result ou un ControlFlow, tel que Result. Cela reflète le fait que ces types d'erreurs ne peuvent pas réellement se produire, ce qui réduit les vérifications inutiles pendant le développement.

Ces mises à jour fondamentales s'accompagnent de modifications importantes dans la gestion des attributs intégrés à Rust. Le compilateur traite désormais les attributs de manière plus stricte et génère des diagnostics plus clairs et plus cohérents. Par exemple, les arguments macro_export incorrects dans les dépendances sont désormais considérés comme des lints refusés par défaut, ce qui rend ces problèmes plus visibles lors de la compilation.


Voici les principales mises à jour de la version 1.92.0 :

Lignes de code never type refusées par défaut

Les équipes chargées du langage et du compilateur continuent de travailler à la stabilisation du type never. Dans cette version, les lignes de code never_type_fallback_flowing_into_unsafe et dependency_on_unit_never_type_fallback, compatibles avec les versions futures, ont été refusées par défaut, ce qui signifie qu'elles provoqueront une erreur de compilation lorsqu'elles seront détectées.

Il convient de noter que même si cela peut entraîner des erreurs de compilation, il s'agit toujours d'un lint ; ces lints peuvent tous être #[allow]. Ces lints ne se déclencheront également que lors de la compilation directe des crates concernées, et non lorsqu'elles sont compilées en tant que dépendances (bien qu'un avertissement soit signalé par Cargo dans de tels cas).

Ces lints détectent le code susceptible d'être rompu par la stabilisation du type never. Il est fortement recommandé de les corriger s'ils sont signalés dans votre graphique de crate.

Nous estimons qu'environ 500 crates sont concernées par ce lint. Malgré cela, nous pensons que cela est acceptable, car les lints ne constituent pas un changement radical et permettront de stabiliser le type never à l'avenir.

unused_must_use n'affiche plus d'avertissement concernant Result<(), UninhabitedType>

Le lint unused_must_use de Rust affiche un avertissement lorsque la valeur de retour d'une fonction est ignorée, si la fonction ou son type de retour est annoté avec #[must_use]. Par exemple, cela avertit si vous ignorez un type de retour de Result, pour vous rappeler d'utiliser ?, ou quelque chose comme .expect(« ... »).

Cependant, certaines fonctions renvoient Result, mais le type d'erreur qu'elles utilisent n'est en fait pas « habité », ce qui signifie que vous ne pouvez construire aucune valeur de ce type (par exemple, les types ! ou Infallible).

Le lint unused_must_use n'affiche désormais plus d'avertissement sur Result<(), UninhabitedType> ou sur ControlFlow<UninhabitedType, ()>. Par exemple, il n'affichera pas d'avertissement sur Result<(), Infallible>. Cela évite d'avoir à vérifier une erreur qui ne peut jamais se produire.

Code Rust : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
  
use core::convert::Infallible; 
fn can_never_fail() -> Result<(), Infallible> { 
    // ... 
    Ok(()) 
} 
  
fn main() { 
    can_never_fail(); 
}


Ceci est particulièrement utile avec le modèle courant d'un trait associé à un type d'erreur, où le type d'erreur peut parfois être infaillible :

Code Rust : Sélectionner tout
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
  
trait UsesAssocErrorType { 
    type Error; 
    fn method(&self) -> Result<(), Self::Error>; 
} 
  
struct CannotFail; 
impl UsesAssocErrorType for CannotFail { 
    type Error = core::convert::Infallible; 
    fn method(&self) -> Result<(), Self::Error> { 
        Ok(()) 
    } 
} 
  
struct CanFail; 
impl UsesAssocErrorType for CanFail { 
    type Error = std::io::Error; 
    fn method(&self) -> Result<(), Self::Error> { 
        Err(std::io::Error::other("something went wrong")) 
    } 
} 
  
fn main() { 
    CannotFail.method(); // No warning 
    CanFail.method(); // Warning: unused `Result` that must be used 
}


Émettre des tables de déroulement même lorsque -Cpanic=abort est activé sous Linux

Les backtraces avec -Cpanic=abort fonctionnaient auparavant dans Rust 1.22, mais ne fonctionnaient plus dans Rust 1.23, car nous avons cessé d'émettre des tables de déroulement avec -Cpanic=abort. Dans Rust 1.45, une solution de contournement sous la forme de -Cforce-unwind-tables=yes a été stabilisée.

Dans Rust 1.92, les tables de déroulement seront émises par défaut même lorsque -Cpanic=abort est spécifié, ce qui permettra aux backtraces de fonctionner correctement. Si les tables de déroulement ne sont pas souhaitées, les utilisateurs doivent utiliser -Cforce-unwind-tables=no pour désactiver explicitement leur émission.

Valider l'entrée vers #[macro_export]

Au cours des dernières versions, de nombreux changements ont été apportés à la manière dont les attributs intégrés sont traités dans le compilateur. Cela devrait considérablement améliorer les messages d'erreur et les avertissements fournis par Rust pour les attributs intégrés et, surtout, rendre ces diagnostics plus cohérents parmi les plus de 100 attributs intégrés.

Pour donner un petit exemple, dans cette version en particulier, Rust est devenu plus strict dans la vérification des arguments autorisés pour macro_export en mettant à niveau cette vérification vers un « lint par défaut refusé » qui sera signalé dans les dépendances.

Source : Annonce Rust 1.92

Et vous ?

Pensez-vous que cette mise à jour est crédible ou pertinente ?
Quel est votre avis sur le sujet ?

Voir aussi :

La version 1.91 du langage de programmation Rust est disponible, ajoutant la prise en charge de niveau 1 pour la plateforme Windows ARM64, les avertissements de pointeurs bruts, et plus encore

La fondation Rust annonce la création d'un fonds pour les mainteneurs afin d'assurer la continuité et de soutenir les rôles à long terme des développeurs qui rendent possible le langage de programmation Rust

C'est désormais officiel : Rust dans le noyau Linux sort du cadre expérimental. Le Rust vient de faire l'objet d'intégration comme partie essentielle du kernel aux côtés du toujours présent langage C
Vous avez lu gratuitement 18 952 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de Uther
Expert éminent sénior https://www.developpez.com
Le 16/12/2025 à 6:14
Citation Envoyé par OuftiBoy Voir le message
De ce que je comprend, il est là pour "signaler" qu'une fonction ne retourne pas de valeur. Mais, pourquoi faut-il signaler au compilateur qu'une fonction ne retourne pas de valeur ? SI une fonction n'a pas a retourner de valeur, le compilateur pourrait le détecter car il n'y aurait dans ce cas pas de 'return' dans le code de la fonction ? (ou de yield dans le cas d'une coroutine).
Une fonction qui retourne ! fait bien plus que ne pas retourner de valeur : elle indique explicitement qu'elle ne se terminera jamais, soit parce qu'elle boucle indéfiniment, soit parce qu'elle termine le fil d’exécution (panique ou exit). Le point d’exclamation n'est pas pour le moment un type a part entière (il n'est utilisable qu'en retour de fonction), mais le but est qu'il devienne le moyen standard de représenter un type qui ne peut exister. Actuellement on utilise pour ça les énumération vides qui partagent certains points commun, notamment le fait de ne pas pouvoir être instanciées.

Ces types ont diverses particularités permises par le fait que leur utilisation concrète est impossible. Par exemple le type ! est automatiquement convertissable en n'importe quel autre type. Ca a de vrai usages sans qu'on s'en rende forcément compte.
Le code suivant ne fonctionnerait pas si la macro panic qui interrompt l’exécution ne retournait pas !, car tous les éléments du match sont censés être du même type.
Code : Sélectionner tout
1
2
3
4
5
6
 
    let texte = match chiffre {
        1 => "un",
        2 => "deux",
        _ => panic!("nombre non supporté"),
    };
Un autre particularité est que les variant d'une énumération contenant un type non instanciable ne sont pas utilisables non plus, donc qu'il n'y a pas besoin de les traiter dans les cas de pattern matching exhaustif:
Code : Sélectionner tout
1
2
3
 
    let nombre: Result<u32,!> = Ok(10);
    let Ok(x) = nombre;  // pas besoin de gérer le cas Err car le type `!` le rend impossible.
Citation Envoyé par OuftiBoy Voir le message
En pascal, on différence cela via une procédure (qui ne retourne pas de valeur), et une fonction qui elle retourne une valeur (je ne sais plus si elle DOIT ou si elle PEUT) retourner une valeur.
En Rust comme en C, il n'y a pas de différenciation entre fonctions et procédures au niveau de la syntaxe, mais il y a bien des fonctions sans type de retour spécifié. En fait ces fonctions retournent implicitement le type unité : (). Ce type unité ne contient aucune valeur (il a une taille de zéro octets), mais il reste et instanciable, contrairement au type ! et aux énumération vides.
2  0 
Avatar de OuftiBoy
Membre éprouvé https://www.developpez.com
Le 15/12/2025 à 16:17
à tous,

Je ne suis pas un expert en Rust, mais j'ai du mal a comprendre l'intérêt du type ! (never), que je viens de découvrir via le post précédent.

De ce que je comprend, il est là pour "signaler" qu'une fonction ne retourne pas de valeur. Mais, pourquoi faut-il signaler au compilateur qu'une fonction ne retourne pas de valeur ? SI une fonction n'a pas a retourner de valeur, le compilateur pourrait le détecter car il n'y aurait dans ce cas pas de 'return' dans le code de la fonction ? (ou de yield dans le cas d'une coroutine).

En pascal, on différence cela via une procédure (qui ne retourne pas de valeur), et une fonction qui elle retourne une valeur (je ne sais plus si elle DOIT ou si elle PEUT) retourner une valeur.

De même, le compilateur pourrait refuser de compiler du code qui ne récupère pas la/les valeurs de retour, non ? N'a-t-il pas toutes les informations nécessaires pour le faire ?

J'ai tenté quelques recherches, mais je n'ai pas été convaincu de son intérêt (ou, ce qui est fort possible, que je ne l'ai pas compris, soit parce que l'explication n'était pas clair, soit parce que l'explication était trop compliquée pour moi).

Merci d'avance pour vos lumières.

BàV et Peace & Love.
1  0 
Avatar de OuftiBoy
Membre éprouvé https://www.developpez.com
Le 15/12/2025 à 21:23
Citation Envoyé par gta126 Voir le message
En réalité ! signifie qu'à la fin de la function, le code ne va pas reprendre chez l'appelant.

Un exemple est la fonction std::process::exit ( https://doc.rust-lang.org/std/process/fn.exit.html ) dont la signature est pub fn exit(code: i32) -> !

Si tu as un code tel que :
fn main() {
println!("hello");
std::process::exit(0);
println!("world");
}

World ne sera pas exécuté car la fonction exit ne reviendra jamais dans la fonction main.
Ok, mais dans ce cas, pourquoi ne pas faire un simple 'return' ou 'exit'.

c'est un peut comme si je fais en 'C'

Code : Sélectionner tout
1
2
3
4
5
6
 
fct() {
    code...
    return
    code...
}
Le compilateur peut détecter que ce qui se trouve après le 'return' ne sera pas exécuté, non ?

Citation Envoyé par gta126 Voir le message
Cela permet de faciliter les analyse de code pour déterminer que le code suivant cette appel de function ne sera jamais exécuté.
Ça permet également d'éviter de devoir faire un exit(0) suivi d'un return <quelque chose> si l'on veut quitter le programme directement depuis une fonction.

Une fonction ne renvoyant pas de valeur fn ma_fonction() {} correspond en réalité à fn ma_fonction() -> () {}
Dans ce cas, à la fin de l'exécution de cette fonction, le flux de code continuera à partir de la prochaine instruction dans la fonction appellante.

fn main() {
println!("hello");
ma_fonction();
println!("world");
}

Ici hello et world seront bien afficher dans la console.

Désolé pour le formatage de mon messgae. Je n'ai pas trouvé comment faire sur téléphone.
Merci de la réponse, mais je trouve (ce n'est que mon avis), qu'on utilise un canon pour tuer une mouche...

Ou alors il y a là quelque chose de plus profond dont je ne capte pas l'usage ou l'utilité.
1  0 
Avatar de fdecode
Membre habitué https://www.developpez.com
Le 16/12/2025 à 11:07
Citation Envoyé par OuftiBoy Voir le message
Merci de la réponse, mais je trouve (ce n'est que mon avis), qu'on utilise un canon pour tuer une mouche...

Ou alors il y a là quelque chose de plus profond dont je ne capte pas l'usage ou l'utilité.
D'autres l'ont évoqué, mais les expressions Rust produisent une valeur avec un type. Notamment, l'usage de return n'est pas nécessaire à la fin d'une fonction Rust, puisque la valeur de la dernière expression est retournée.
Le typage est un mécanisme profond en Rust, et intervient également dans la sémantique de prêt. Le type ! existe depuis longtemps (en nightly) et contribue à la pleine cohérence du système de typage: il permet de typer un horizon indépassable: arrêt, panique, boucle infinie, ...
C'est aussi le type minimal en Rust au sens où il peut être converti en n'importe quel type.

À quoi cela peut servir concrètement? Je n'utilise pas le type ! personnellement, et ma réponse est forcément limitée. À certaines simplifications dans le langage, comme indiqué par Uther. Par ailleurs, il permet éventuellement plus de flexibilité dans la mise en œuvre des panic! / exit / todo! / ..., par exemple, y compris dans un contexte multithread [playground]:
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#![feature(never_type)]
use std::{ process::exit, thread::spawn };
 
fn div<F: Fn() -> ! + Send + Sync>(left: f64, right: f64, failure: F) -> f64 {
    if right == 0.0 { failure(); } else { left / right }
}
 
fn panique1() -> ! { panic!("panique 1!"); }
fn panique2() -> ! { panic!("panique 2!"); }
 
fn main() {
    for h in [ 
        spawn(|| println!("f64 -> {}", div(32.0, 5.0, panique1))),
        spawn(|| println!("f64 -> {}", div(32.0, 0.0, panique2))),
        spawn(|| println!("f64 -> {}", div(128.0, 16.0, || todo!()))),
        spawn(|| println!("f64 -> {}", div(8.0, 0.0, || unreachable!())))
    ] { let _ = h.join(); }
    println!("f64 -> {}", div(12.0, 5.0, ||exit(0)));
}
1  0 
Avatar de Uther
Expert éminent sénior https://www.developpez.com
Le 16/12/2025 à 19:11
En effet c'est un peu compliqué car la situation est en train de changer.
Actuellement si on utilise le compilateur stable, ! n'est utilisable qu'en type de retour d'une fonction qui ne peut pas se terminer. On ne peut pas encore l'utiliser directement comme type(ou composant d'un type) d'une variable. Mais dans un avenir proche (ou actuellement si on active la fonctionnalité expérimentale sur la version nightly du compilateur), le type ! sera utilisable partout en tant que type.

Comme dit imperio, ça sert en partie d'indication : on a l'assurance qu'une fonction qui retourne ! ne se terminera pas. Mais ça aussi de vraies incidences techniques comme j'ai essayé de le montrer dans mes exemples.
Il faut se représenter le type ! comme un type non instanciable, qui sert a définir des cas impossibles. Dans mon second exemple, le type Result<u32,!> indique un type qui ne peux pas avoir de cas d'erreur. Si on essaie de lui affecter une erreur, il refusera de compiler et du coup on a plus a traiter ce cas.
1  0 
Avatar de Uther
Expert éminent sénior https://www.developpez.com
Le 19/12/2025 à 0:12
Citation Envoyé par OuftiBoy Voir le message
C'est en effet une des choses les plus difficiles avec Rust, ses changements et avancées petit bout par petit bout. Après 11 ans de développement, je pense qu'avec les moyens qu'ils ont, plus de "fonctionnalités" auraient déjà dû être "tranchées". Je pense que l'équipe derrière Rust devrait faire moins de "release" (tous les 2 ou 3 ans), et pas avoir une nouvelle version trop souvent avec une poignée de petits ajouts.
Rust n'évolue pas plus ni moins que la plupart des langages actifs comme Java, C++, C#, Go, ...
Il faut voir que ajouter des fonctionnalités à un langage un minimum complexe, ça n'est pas anodin. Il faut s'assurer qu'il n' y a pas d'interactions négatives avec toutes les autres fonctionnalités, et qu'il n'y aura pas de mauvaises surprises à l'usage. C'est important de ne pas faire d'erreur car c'est difficile voire impossible dans certains cas de faire marche arrière une fois la fonctionnalité officialisée. Si les fonctionnalités restent un moment en mode expérimental, c'est justement pour être sur qu'elles ne posent pas de problèmes.

Citation Envoyé par OuftiBoy Voir le message
Je comprend bien la nécessité d'un mode "expérimentale", mais ce n'est pas une raison pour sortir une nouvelle "spécification" du langage tous les 3 mois, je pense que l'équipe de Rust doit se poser la question, car vu de l'extérieur, on a l'impression que "ça part dans tous les sens". Un développeur "normal" (pas un 'insider'), devrait pouvoir se reposer sur une spec bien précise et qui a une durée de vie plus grande que la durée de sa lecture
Sauf que ton appréciation de la situation n'est pas du tout bonne, Rust ne change pas intégralement tous les quatre matins. En fait, rien n'a changé au sujet du type ! sur la version stable du compilateur depuis la sortie du langage il y a plus de 10 ans. La fonctionnalité est développé dans les version expérimentales de Rust dont l'utilisation n'est pas recommandé pour les utilisateurs finaux.
Ce qui a été ajouté dans cette version stable de Rust, c'est juste des messages d'avertissement pour permettre de repérer et adapter des aujourd'hui des cas d'erreurs qui pourraient apparaitre quand le type `!` sera stabilisé.

Citation Envoyé par OuftiBoy Voir le message
J'ai du mal avec la notion de "définir des cas impossibles", quel est l'intérêt d'une telle chose ?. Et si le type Result<u32, !> refuse de compiler, je ne comprend pas sa nécessité, car il y a plus simple, pour un type donné, de détecter et refuser qu'on lui affecte une valeur qu'il ne peut pas accepter.
Une variable de type Result<Valeur, Erreur> contient soit une valeur de type Valeur ou une erreur de type Erreur. C'est le moyen le plus courrant en Rust de retourner la valeur d'une opération qui peut échouer.
Donc la variable de type Result<u32, !> de mon exemple est tout a fait utilisable, c'est juste qu'elle ne pourra pas contenir de cas d'erreur, seulement une valeur valide de type u32.

Citation Envoyé par OuftiBoy Voir le message
Sans prétention ni sans vouloir me "comparer" avec l'équipe de Rust, ...
En effet la vérification du domaine de validité d'une variable est une fonctionnalité intéressante. Il y a 'ailleurs des gens qui expérimentent comment apporter ça dans Rust, mais là, on est pas du tout dans le même sujet.

Citation Envoyé par OuftiBoy Voir le message
Je cromprend bien cela, je suis juste étonné qu'on utilise un opérateur pour cela.
Il ne s'agit pas d'un opérateur, mais juste d'un type spécial qui permet certaine largesses.

Citation Envoyé par OuftiBoy Voir le message
C'est bien normal, mais je ne parlais pas de "limitation de fonctionnalité", c'était plutôt dans le sens de "rétention" d'un concept durant 5 ans, en "nightly". Je trouve que c'est assez lent pour se décider si on l'autorise ou pas, c'est plutôt cela qui m'intrigue. Et je suis bien d'accord cependant que certains concepts ne sont pas facile a maîtriser, j'en suis la preuve vivante ;-)
5 ans, pour un langage comme le Rust c'est pas forcément très long. Il faut voir que un langage comme Rust, relativement complexe, et qui tient à assurer le plus possible la compatibilité ascendante, il faut être prudent et composer avec l'existant.

Citation Envoyé par OuftiBoy Voir le message
Ok, ok, je comprend cela, mais Rust a un objectif bien précis (c'est du moins l'impression que j'en ai), c'est de "prendre le pas" sur le C++. Et je trouve étonnant que des changements "en profondeur" sont encore nécessaires après 11 ans de développement avec des moyens relativement important, alors qu'ils savaient "que qui n'allait pas" avec le C++ et savaient ce qu'ils voulaient faire.
Il a été prévu des le début que le type never serait généralisé plus tard, mais l'équipe de Rust n'est quand même pas illimitée, il y a de nombreux points sur lesquels elle travaille. Le type never n'est clairement pas une urgence.

Citation Envoyé par OuftiBoy Voir le message
on a l'assurance qu'une fonction qui retourne ! ne se terminera pas. De mon point de vue, je ne vois pas comment une fonction peut "en-même tant" ne pas se terminer ET "en-même tant" retourner le type !
Ce n'est pas parce que la fonction ne se terminera pas au moment de l’exécution qu'elle n'a pas un type de retour défini dans le code.
1  0 
Avatar de gta126
Membre actif https://www.developpez.com
Le 15/12/2025 à 20:10
En réalité ! signifie qu'à la fin de la function, le code ne va pas reprendre chez l'appelant.

Un exemple est la fonction std::process::exit ( https://doc.rust-lang.org/std/process/fn.exit.html ) dont la signature est pub fn exit(code: i32) -> !

Si tu as un code tel que :

Code : Sélectionner tout
1
2
3
4
5
fn main() {
    println!("hello");
    std::process::exit(0);
    println!("world");
}
World ne sera pas exécuté car la fonction exit ne reviendra jamais dans la fonction main.

Cela permet de faciliter les analyse de code pour déterminer que le code suivant cette appel de function ne sera jamais exécuté.
Ça permet également d'éviter de devoir faire un exit(0) suivi d'un return <quelque chose> si l'on veut quitter le programme directement depuis une fonction.

Une fonction ne renvoyant pas de valeur fn ma_fonction() {} correspond en réalité à fn ma_fonction() -> () {}
Dans ce cas, à la fin de l'exécution de cette fonction, le flux de code continuera à partir de la prochaine instruction dans la fonction appellante.

Code : Sélectionner tout
1
2
3
4
5
fn main() {
    println!("hello");
    ma_fonction();
    println!("world");
}
Ici hello et world seront bien afficher dans la console.

Désolé pour le formatage de mon message. Je n'ai pas trouvé comment faire sur téléphone.
0  0 
Avatar de imperio
Membre chevronné https://www.developpez.com
Le 16/12/2025 à 0:43
Le ! est un type qui indique que le programme s'arrêtera dans cette fonction. Ça permet de le savoir dès la lecture du code ou de la doc sans avoir besoin de lire le code de la fonction.

Donc non, pas comme en C/C++ et consorts. C'est un confort en somme.
0  0 
Avatar de OuftiBoy
Membre éprouvé https://www.developpez.com
Le 16/12/2025 à 16:51
,

Merci pour vos réponses, il va me falloir un peu de temps pour les assimiler. Il me semble que ! a plusieurs "fonctionnaltés" qui n'ont pas forcément de relation entres-elles. J'ai repris les extraits qui m'ont "marqués" de vos réponses:

imperio: Le ! est un type qui indique que le programme s'arrêtera dans cette fonction. Donc non, pas comme en C/C++ et consorts. C'est un confort en somme.
Uther:Le point d’exclamation n'est pas pour le moment un type a part entière (il n'est utilisable qu'en retour de fonction).
fdecode;il permet de typer un horizon indépassable: arrêt, panique, boucle infinie, ...
fdecode:C'est aussi le type minimal en Rust au sens où il peut être converti en n'importe quel type.
Pour imperio ! est un type "de confort" pour dire que le programme s'arrêtera dans la fonction. Uther ajoute que ce n'est pas pour le moment un type "a part entière" car il n'est utilisable qu'en retour de fonction. fdecode ajoute qu'il permet de typer un "horizon indépassable" (là j'ai du mal a saisir, que veut dire "typer un horizon (indépassable)", et que c'est le "type minimal" (donc, c'est un type ou pas ?), et qu'il peut être "converti en n'importe quel type.

C'est pas mal d'usages pour un simple petit opérateur ! Je vous avoue que ce n'est pas facile a décrypter. C'est pas vraiment un type, mais c'est le type de base (un peu comme un "généric" ?) Utiliser un "type" pour indiquer qu'un programme s'arrêtera dans la fonction qui l'utilise, j'ai du mal a comprendre le "concept" caché derrière.

Je vais potasser tout cela

Encore merci à vous.
BàV et Peace & Love.

(PS: la définition de mon language Home se précise de plus en plus et j'avance bien, je n'ai pas abandonné, mais j'ai un peu "refactorisé" le code, et tant le langage que son compilateur se peaufine tout doucement. J'ai aussi commencer à attaquer la VM et le jeu d'instruction du CPU virtuel. Y'a encore du boulot, mais il me semble que j'avance sans trop de soucis, c'est juste beaucoup a faire...)
0  0 
Avatar de OuftiBoy
Membre éprouvé https://www.developpez.com
Le 17/12/2025 à 11:44
Uther,

Citation Envoyé par Uther Voir le message
En effet c'est un peu compliqué car la situation est en train de changer.
C'est en effet une des choses les plus difficiles avec Rust, ses changements et avancées petit bout par petit bout. Après 11 ans de développement, je pense qu'avec les moyens qu'ils ont, plus de "fonctionnalités" auraient déjà dû être "tranchées". Je pense que l'équipe derrière Rust devrait faire moins de "release" (tous les 2 ou 3 ans), et pas avoir une nouvelle version trop souvent avec une poignée de petits ajouts.

Citation Envoyé par Uther Voir le message
Actuellement si on utilise le compilateur stable, ! n'est utilisable qu'en type de retour d'une fonction qui ne peut pas se terminer. On ne peut pas encore l'utiliser directement comme type(ou composant d'un type) d'une variable. Mais dans un avenir proche (ou actuellement si on active la fonctionnalité expérimentale sur la version nightly du compilateur), le type ! sera utilisable partout en tant que type.
Je comprend bien la nécessité d'un mode "expérimentale", mais ce n'est pas une raison pour sortir une nouvelle "spécification" du langage tous les 3 mois, je pense que l'équipe de Rust doit se poser la question, car vu de l'extérieur, on a l'impression que "ça part dans tous les sens". Un développeur "normal" (pas un 'insider'), devrait pouvoir se reposer sur une spec bien précise et qui a une durée de vie plus grande que la durée de sa lecture

Citation Envoyé par Uther Voir le message
Comme dit imperio, ça sert en partie d'indication : on a l'assurance qu'une fonction qui retourne ! ne se terminera pas. Mais ça aussi de vraies incidences techniques comme j'ai essayé de le montrer dans mes exemples.
Et je t'en remercie, sois en certains.

Citation Envoyé par Uther Voir le message
Il faut se représenter le type ! comme un type non instanciable, qui sert a définir des cas impossibles. Dans mon second exemple, le type Result<u32,!> indique un type qui ne peux pas avoir de cas d'erreur. Si on essaie de lui affecter une erreur, il refusera de compiler et du coup on a plus a traiter ce cas.
J'ai du mal avec la notion de "définir des cas impossibles", quel est l'intérêt d'une telle chose ?. Et si le type Result<u32!> refuse de compiler, je ne comprend pas sa nécessité, car il y a plus simple, pour un type donné, de détecter et refuser qu'on lui affecte une valeur qu'il ne peut pas accepter.

Sans prétention ni sans vouloir me "comparer" avec l'équipe de Rust, dans mon langage Home, il n'y a au départ "aucun type" de base. Le compilateur génère cependant les cas les plus courant. Penons int8 par exemple. Avec le type int8, faire int8 a=300 sera refusé par le compilateur. Il est refusé car le compilateur sait que int8 ne peut contenir que des valeurs de 0~255. Si tu a int8 a, int16 b, faire un a := b sera refusé, car on tente d'affecter à une variable de type int8 une variable qui peut contenir une valeur > 255. Ces cas sont détectés à la compilation.

Si tu as int8 a=255, et que tu fais a := a + 1, cela sera également refusé, car la partie Analyseur Static (en développement) détectera (toujours à la compilation) que l'opération donnera à int8 a une valeur "hors range". Pareil dans une boucle, où le nombre de boucle possible est déterminé, et que la possibilité d'affecter à int8 a une valeur >255 est "possible". Et le code sera refusé à la compilation.

Aussi, lorsque l'utilisateur veut créer un type, disons intRoue, il (pourra) le faire via un def int Roue[1..4] et il sera impossible de faire intRoue nb=5 ni même nb := 0, car 5 et 0 sont "hors range" pour le type intRoue. Le compilateur et son copain l'Analyseur Static empêcherons ces cas à la compilation. (c'est du moins comme cela que je compte rendre le typage très "strict"). Je vais peut-être tomber sur des cas impossible a détecter, mais je ne vois pas lesquels pour le moment. C'est certains que l'analyseur static sera d'une importance capital pour que cela fonctionne à 100%.

Je m'excuse d'être un rien "Hors Sujet", mais c'était pour énoncer des cas pour lequel le type ! de Rust joue un rôle.

Ce ne sont que mes réflexions, et c'est en développement, donc je ne peux pas assurer que j'atteindrais mon but de la sorte, j'en suis bien conscient, j'expérimente...

BàV et Peace & Love.
0  0