[Pense-bête] Rust / Python : le cas des strings / str (chaîne de caractères)
par
, 05/03/2020 à 13h12 (788 Affichages)
Pour ce pense-bête, penchons-nous tout d'abord dans un nouveau projet :
... en pensant à ajouter dans le fichier toml de ce projet :
Code : Sélectionner tout - Visualiser dans une fenêtre à part cargo new projet
... et éditer le fichier (préalablement créé) lib.rs dans le dossier ./src :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 [lib] name = "projet" crate-type = ["dylib"]
Code rust : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 use std::ffi::CString; use std::ffi::CStr; use std::os::raw::c_char; #[no_mangle] // pub extern fn depuis_rust() -> *const c_char { let s = CString::new("& oui").unwrap(); let p = s.as_ptr(); std::mem::forget(s); p } #[no_mangle] pub extern fn vers_rust( texte: *const c_char) -> *const c_char { let cstr = unsafe { CStr::from_ptr( texte ) }; let string = cstr.to_str().expect("chaine UTF-8 invalide"); println!("{}", string); let s = CString::new("@ maison").unwrap(); let p = s.as_ptr(); std::mem::forget(s); p }
... puis lancer la compilation :
Vous obtenez le fichier .so correspondant. Éditons maintenant le fichier .py qui utilisera cette bibliothèque :
Code : Sélectionner tout - Visualiser dans une fenêtre à part cargo build --release
Code python3 : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 import ctypes lib = ctypes.cdll.LoadLibrary("./target/release/libembed.so") l = lib.depuis_rust() print( ctypes.c_char_p( l ).value ) l2 = lib.vers_rust( ctypes.c_char_p( "tout ça : ²&é\"'(-è_çà)=".encode('utf-8') ) ) print( ctypes.c_char_p( l2 ).value )
L'exécution du script Python devrait retourner alors les chaînes attendues :
Quelques observations :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 b'& oui' tout ça : ²&é"'(-è_çà)= b'@ maison'
- la gestion des chaînes "via" le format utilisé par C (le caractère \0 en fin de chaîne) ;
- l'usage "natif" d'UTF-8 ;
- l'inadéquation de la gestion habituelle de Rust sur du texte (vecteur) avec ce qui est supporté par ctypes dans Python (évolution à venir ?) ;
- l'entête "no_mangle" (aucune mutilation) dans Rust, permet de respecter les standards du C lors de la compilation.
Un dernier point : l'usage de std::mem::forget peut sembler singulier au sein de depuis_rust. On demande explicitement "l'oubli" de la variable "s" dont on a demandé le pointeur juste avant (et on retourne ce pointeur). Pourquoi un tel comportement ? L'explication est logique : il s'agit d'abord de préserver l'intégrité des règles (de toute façon incontournables...) de Rust - qui est aussi sa sécurité et sa force.
La variable "s" utilisée ici, est liée au contexte de la fonction. L'usage de std::mem::forget "libère" en quelque sorte le pointeur, et peut donc être utilisée ailleurs (cf. règle de propriété / partage).
Voir aussi :