CoddityTeam / movaicode

Concours mensuel du Pire Développeur de France
122 stars 10 forks source link

Rust. Bien utilisé; ça peut être un merveilleux langage. Ça peut sinon être utilisé comme un dev C. (movaicode 18) #282

Open NeoGalaxy opened 1 year ago

NeoGalaxy commented 1 year ago

Que tout rustacé s'accroche bien, ça ne ressemble à rien de ce qu'ils ont connu:

use std::mem::transmute as aaa;

use derive as aaa;
use panic as print;
use std::fmt::Debug as aaaa;
use std::fmt::Display as aaaaa;

type Signe<'a> = &'a str;

#[aaa(aaaa)]
#[repr(u8)]
enum Date {
    Belier,
    Taureau,
    Gemeaux,
    Cancer,
    Lion,
    Vierge,
    Balance,
    Scorpion,
    Sagittaire,
    Capricorne,
    Verseau,
    Poissons,
}

#[repr(C)]
struct A {
    aaaaa: *const u8,
    a: u8,
    aa: u8,
    #[cfg(target_pointer_width = "64")]
    aaa: u8,
    #[cfg(target_pointer_width = "64")]
    aaaa: u8,
}

fn date_vers_signe(table: Signe) -> Date {
    // On "parse" la valeur
    let mut a: A = unsafe { aaa(table) };
    // On vérifie la taille
    if cfg!(target_pointer_width = "64") {
        if a.a != 10 || a.aa != 0 {
            print!("date_vers_signe ne fonctionne que sur des strings de taille 10")
        }
    } else {
        if a.a != 10 || a.aa != 0 || a.aaa != 0 || a.aaaa != 0 {
            print!("date_vers_signe ne fonctionne que sur des strings de taille 10")
        }
    }

    // Récupère le caractère d'indice i dans le tableau qui commence à ptr
    let mut aa = |ptr: *const u8, i| unsafe { aaa::<_, char>(*ptr.add(aaa(i as isize)) as u32) };

    // Vérifie que c est un chiffre
    let mut aaaa = |c| c <= '9' && c >= '0';

    if !aaaa(aa(a.aaaaa, 0))
        || !aaaa(aa(a.aaaaa, 1))
        || aa(a.aaaaa, 2) != '/'
        || !aaaa(aa(a.aaaaa, 3))
        || !aaaa(aa(a.aaaaa, 4))
        || aa(a.aaaaa, 5) != '/'
        || !aaaa(aa(a.aaaaa, 6))
        || !aaaa(aa(a.aaaaa, 7))
        || !aaaa(aa(a.aaaaa, 8))
        || !aaaa(aa(a.aaaaa, 9))
    {
        print!("invalid fromat")
    }

    // traduit un char en entier
    let mut aaaaa = |c| unsafe { aaa::<_, u32>(c) - aaa::<_, u32>('0') };

    let mut jour: u32 = aaaaa(aa(a.aaaaa, 0)) * 10 + aaaaa(aa(a.aaaaa, 1));
    let mut mois: u32 = aaaaa(aa(a.aaaaa, 3)) * 10 + aaaaa(aa(a.aaaaa, 4));
    let mut année: u32 = aaaaa(aa(a.aaaaa, 6)) * 1000
        + aaaaa(aa(a.aaaaa, 7)) * 100
        + aaaaa(aa(a.aaaaa, 8)) * 10
        + aaaaa(aa(a.aaaaa, 9));

    if mois < 1 || mois > 12 {
        print!("Mois invalide");
    }

    let bisextile = |a| a % 4 == 0 && a % 100 != 0 || a % 1000 == 0;

    if jour < 1
        || (mois == 2 && (jour > 29 || bisextile(année) && jour > 28)
            || ![1, 3, 5, 7, 8, 10, 12].contains(&mois) && jour > 30
            || jour > 31)
    {
        print!("Jour invalide");
    }

    if jour < 21 {
        mois -= 1
    }
    mois -= 3;
    if unsafe { aaa::<_, i32>(mois) } < 0 {
        mois += 12;
    }
    let mois = mois as u8;
    unsafe { aaa(mois) }
}

Si vous voulez le tester, vous pouvez le tester sur le playground en ligne. Changez juste la date dans la fonction main et cliquez sur "run" en haut à droite. ⚠️ Attention, il faut que ce soit compilé en mode release (ce qui est fait dans ce playground) pour éviter de voir des erreurs d'overflow quand on teste les premiers mois.


Je comprends pas le code......

D'acc ! Et bien voici l'explication de différentes parties du code, avec celles-ci tu devrait comprendre. Dis-moi si jamais tu ne sais pas ce qu'est un pointeur par contre, parce que sinon il y a une partie que tu vas pas comprendre.

use std::mem::transmute as aaa;
use derive as aaa;
use panic as print;
use std::fmt::Debug as aaaa;
use std::fmt::Display as aaaaa;

Là, je renomme des éléments pour la suite:

type Signe<'a> = &'a str;

Juste pour apporter de la confusion.

#[aaa(aaaa)]
#[repr(u8)]
enum Date { ... }

#[repr(u8)] dit que l'énum défini ici sera écrit sur 8 octets.

#[repr(C)]
struct A {...}

Rust, l'agencement en mémoire du contenu d'une structure n'est pas définie. #[repr(C)] dit d'utiliser celle de C.

Pour le struct A, ça définit une structure (un peut équivalent à des classes sans méthodes pour ceux qui connaissent pas, ou à des objets pour ceux qui viennent de JS) et j'ai eu la flemme de chercher un nom. La structure a un champ définissant un pointeur et 2 à 4 autres champs pour contenir l'information de la taille du string de départ.

#[cfg(target_pointer_width = "64")]
aaa: u8,

En rust, on a un type pour les entiers de la même taille que les pointeurs, qui s'appelle usize. En fonction de l'architecture, usize est soit de 64bits, soit de 32bits. #[cfg(target_pointer_width = "64")] dit "inclus moi ce qui vient après si usize est de taille 64`.

let mut a: A = unsafe { aaa(table) };

Je vais rappeler ce que fait aaa: ça change le type d'une variable sans changer la mémoire derrière. Là on lui donne en argument une variable de type Signe et on l'affecte à une variable de type A, donc aaa sait de quel type à quel type convertir. S'il ne sait pas, il faut à la pace utiliser la syntaxe aaa::<T, U>(...)T est le type de départ, et U d'arrivée.

C'est une opération dangereuse et le compilo le sait, donc le unsafe est là pour dire "tkt compilo, je sais ce que je fais". D'ailleurs, ce code fontionne parce que pour une raison quelquonque, &str (pointeur vers une chaine de carractères avec une information de la taille de la chaine) et A on a agencement mémoire qui va bien ensemble, mais si un jour Rust décide de changer totalement l'agencement mémoire de &str, ça fera n'importe quoi. Pour avoir un avant-goût de ce qui pourrait se passer, déplacez la première ligne de struct A à la fin, et enlevez la vérification de la taille du string donné en argument.

La bonne manière d'avoir accès au contenu de table est d'utiliser table.len() et table[indice]. Tout simplement.

if cfg!(target_pointer_width = "64")

Simillaire à #[cfg(target_pointer_width = "64")]

ptr.add(aaa(i as isize))

ptr.add(x) fait ptr + x avec xétant un entier de type usize. i est ici de type i32 donc on utilise aaa pour le convertir. Sauf que aaa ne fonctionne que sur des types de taille égales, or usize et i32 ne sont pas forcément de même taille. Je convertis donc i32 en isize d'abord parce que isize est de la bonne taille.

*ptr.add(aaa(i as isize)) est une sorte d'équivalent au ptr[i] en C.

Voilà !! Ça devrait être tout. En vrai, c'est pas extraordinairement moche, mais c'était marrant à faire et ça peut tout de même rendre certains rustacés fous.