Simplifier une fonction pour répéter un caractère

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Suite aux conseils d’un ami, je me met doucement au Rust (et j’avoue que c’est un langage agréable). Voici un code qui me permet de dessiner un rectangle et un triangle rectangle dans ma console :

fn main() {
    draw_rectangle(5, 5, '.');
    draw_right_triangle(5, '.')
}

fn draw_rectangle(width: u32, height: u32, pattern: char) {
    for _ in 0..height {
        println!("{}", repeat(pattern, width));
    }
}

fn draw_right_triangle(height: u32, pattern: char) {
    for row in 0..height {
        println!("{}", repeat(pattern, row));
    }
}

fn repeat(pattern: char, times: u32) -> String {
    let mut result = String::new();
    for _ in 0..times {
        result.push(pattern);
        result.push(' ');
    }

    return result;
}

Ce code me parait compliqué pour ce que ça fait. Dans d’autres langages, un simple '.' * 5 aurait suffit pour répéter un caractère mais là je me retrouve à devoir faire une fonction pour faire ça. Est-ce qu’il y aurait quelque chose qui m’aurait échappé ? Ou alors je ferais mieux d’utiliser des String dans mes fonctions directement plutôt que des caractères ?

Merci pour vos conseils !

+0 -0

Salut !

Je débute Rust à peine moins que toi, mais je pense pouvoir t’aider sur ce coup. :)

En fait le problème est que tu pars d’un caractère, parce que les chaînes exposent directement une méthode repeat (doc ici).

Du coup pour ta fonction draw_rectangle, ça pourrait donner quelque chose comme ça :

fn draw_rectangle(width: usize, height: usize, pattern: &str) {
    for _ in 0..height {
        println!("{}", pattern.repeat(width));
    }
}

Et tu peux l’appeler avec draw_rectangle(5, 5, "* ") pour que ça te donne le même résultat que ton code.

+1 -0

Merci pour ton aide !

En effet, une solution simple est de passé aux String. Mais imaginons le cas où dans mes définitions de fonctions je dois rester avec des char, comment faire ? La bonne solution est celle que j’ai trouvé ?

EDIT : je ne veux pas que l’utilisateur ai à passer la chaîne de caractères . (avec un espace après). J’ai donc fait cette solution avec les strings :

fn main() {
    draw_rectangle(5, 5, ".");
}

fn draw_rectangle(width: usize, height: usize, pattern: &str) {
    for _ in 0..height {
        println!("{}", format!("{} ", pattern).repeat(width));
    }
}

Mais dans ce cas autant utiliser les char non ? J’ai l’impression que ça complique terriblement le code si je souhaite les utiliser… :lol:

EDIT 2 : je n’ai rien dit, la fonction format! accepte également les char !

fn main() {
    draw_rectangle(5, 5, '.');
    draw_right_triangle(5, '.');
}

fn draw_rectangle(width: usize, height: usize, pattern: char) {
    for _ in 0..height {
        println!("{}", format!("{} ", pattern).repeat(width));
    }
}

fn draw_right_triangle(height: usize, pattern: char) {
    for row in 0..height {
        println!("{}", format!("{} ", pattern).repeat(row));
    }
}
+0 -0

Oui les macros format!, println! et consorts fonctionnent avec n’importe quels types de données qui implémentent le trait Display. C’est l’équivalent Rust de surcharger la méthode __str__ en Python, et du coup ça marche avec tous les types primitifs.

+1 -0

Petit HS, mais pour compléter le message de @nohar, il est à noter que {} utilise le trait Display, quand {:?} utilise le trait Debug (que tu peux dériver sans avoir besoin de fournir une implémentation, de ce que je me souviens).

(c’est dans ces cas là que les commentaires de StackOverflow me manquent)

+1 -0

J’ai eu envie de m’amuser avec cet exercice pour résumer un peu tout ce qui s’est dit ici.

use std::fmt::Display;

fn main() {
    draw_rect(6, 4, "*");
    draw_rect(3, 5, '*');
    draw_rect(5, 6, 0);
}

fn draw_rect<T>(width: usize, height: usize, pattern: T)
where
    T: Display,
{
    print!(
        "{}",
        format!("{}\n", format!("{} ", pattern).repeat(width)).repeat(height)
    )
}

(playground)

Alors c’est sûrement un petit peu plus loin dans l’apprentissage que là où en est @Wizix, parce que la fonction draw_rect est générique.

La signature :

fn draw_rect<T>(width: usize, height: usize, pattern: T)
where
    T: Display

Indique que pattern peut être de n’importe quel type à partir du moment où ce type implémente le trait Display (celui de pouvoir être affiché dans une format-string au moyen de "{}"). Ici, vu que le but est vraiment de dessiner quelque chose dans la console, c’est bien ce trait dont on a besoin. Comme @lthms le fait judicieusement remarquer, il existe aussi le trait Debug que l’on peut dériver (= demander à Rust de l’implémenter tout seul automatiquement) sur un type de données custom, et afficher avec "{:?}". Ce trait est plutôt utilisé (comme son nom l’indique) pour afficher des données dans des messages de debug ou d’erreur.

Pour continuer le parallèle avec Python (parce que je vois que tu connais déjà Python), le trait Display est équivalent à surcharger la méthode __str__ d’une classe, là où le trait Debug est équivalent à surcharger __repr__.

Du coup pour illustrer, j’appelle trois fois ma fonction avec des patterns de trois types différents (un &str, un char et un entier quelconque). Ce qui donne l’affichage suivant :

* * * * * * 
* * * * * * 
* * * * * * 
* * * * * * 
* * * 
* * * 
* * * 
* * * 
* * * 
0 0 0 0 0 
0 0 0 0 0 
0 0 0 0 0 
0 0 0 0 0 
0 0 0 0 0 
0 0 0 0 0 
+2 -0

C’est un peu l’idée oui.

La différence principale (par rapport à g++ en tout cas) c’est que le compilo te sort des messages d’erreurs concis et explicites quand tu te plantes. :lol:

L’autre différence c’est que tu peux aussi (et tu es parfois obligé) utiliser cette syntaxe générique pour donner des informations au compilateur sur la durée de vie des références que ta fonction accepte en paramètres/retourne. Mais tu verras ça en temps et en heure : c’est une des notions de Rust les moins évidentes à piger, mais le book explique ça très bien.

+1 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte