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:
std::mem::transmute sera nommé aaa. C'est à peu près l'équivalent d'un cast en C: ça reprend le contenu d'une variable dans un nouveau type, sans changer le contenu de la mémoire. Ça peut avoir un impact très aléatoire en fonctions des types de départ et d'arrivée, et même créer des variables avec une valeur qui ne devrait pas exister.
derive sera nommé aaa. Il n'y a pas grand chose à comprendre, juste que si on met #[derive(Debug)] devant un type (comme Date), ça permet de le print avec println!("{:?}", variable). Ça n'est utile que dans le main sur le playground.
panic sera renommé print. Cet import vise seulement à porter confusion. panic termine le programme avec un message d'erreur.
std::fmt::Debug sera renommé aaaa. utilisé en complément avec aaa, pour que #[derive(Debug)] soit `#[aaa(aaaa)]. Je trouvais ça marrant.
std::fmt::Debug sera renommé aaaaa. Ça ne sert à rien, à part confondre aaaa et aaaaa.
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>(...) où 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.
Que tout rustacé s'accroche bien, ça ne ressemble à rien de ce qu'ils ont connu:
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.
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.
Là, je renomme des éléments pour la suite:
std::mem::transmute
sera nomméaaa
. C'est à peu près l'équivalent d'un cast en C: ça reprend le contenu d'une variable dans un nouveau type, sans changer le contenu de la mémoire. Ça peut avoir un impact très aléatoire en fonctions des types de départ et d'arrivée, et même créer des variables avec une valeur qui ne devrait pas exister.derive
sera nomméaaa
. Il n'y a pas grand chose à comprendre, juste que si on met#[derive(Debug)]
devant un type (commeDate
), ça permet de le print avecprintln!("{:?}", variable)
. Ça n'est utile que dans le main sur le playground.panic
sera renomméprint
. Cet import vise seulement à porter confusion.panic
termine le programme avec un message d'erreur.std::fmt::Debug
sera renomméaaaa
. utilisé en complément avecaaa
, pour que#[derive(Debug)]
soit`#[aaa(aaaa)]
. Je trouvais ça marrant.std::fmt::Debug
sera renomméaaaaa
. Ça ne sert à rien, à part confondreaaaa
etaaaaa
.Juste pour apporter de la confusion.
#[repr(u8)]
dit que l'énum défini ici sera écrit sur 8 octets.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.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 siusize
est de taille 64`.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 typeSigne
et on l'affecte à une variable de typeA
, doncaaa
sait de quel type à quel type convertir. S'il ne sait pas, il faut à la pace utiliser la syntaxeaaa::<T, U>(...)
oùT
est le type de départ, etU
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) etA
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 destruct 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'utilisertable.len()
ettable[indice]
. Tout simplement.Simillaire à
#[cfg(target_pointer_width = "64")]
ptr.add(x)
faitptr + x
avecx
étant un entier de typeusize
.i
est ici de typei32
donc on utiliseaaa
pour le convertir. Sauf queaaa
ne fonctionne que sur des types de taille égales, orusize
eti32
ne sont pas forcément de même taille. Je convertis donci32
enisize
d'abord parce queisize
est de la bonne taille.*ptr.add(aaa(i as isize))
est une sorte d'équivalent auptr[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.