Comment puis-je convertir une chaîne C dans une Rouille de la chaîne et à l'arrière via FFI?
Je vais essayer d'obtenir un C chaîne de caractères retournée par une bibliothèque en C et la convertir en une Rouille de la chaîne via FFI.
mylib.c
const char* hello(){
return "Hello World!";
}
principal.rs
#![feature(link_args)]
extern crate libc;
use libc::c_char;
#[link_args = "-L . -I . -lmylib"]
extern {
fn hello() -> *c_char;
}
fn main() {
//how do I get a str representation of hello() here?
}
Vous devez vous connecter pour publier un commentaire.
La meilleure façon de travailler avec les chaînes C dans la Rouille est d'utiliser les structures de la
std::ffi
module, à savoirCStr
etCString
.CStr
est une façon dynamique de taille moyenne de type et ainsi il ne peut être utilisé par l'intermédiaire d'un pointeur. De ce fait, il est très similaire à la régulièrestr
type. Vous pouvez construire une&CStr
de*const c_char
à l'aide d'un dangereuxCStr::from_ptr
méthode statique. Cette méthode est dangereuse car il n'y a aucune garantie que le pointeur brut vous passer est valide, qu'elle n'est point valable chaîne C et que la chaîne de la durée de vie est correcte.Vous pouvez obtenir un
&str
à partir d'un&CStr
à l'aide de sonto_str()
méthode.Voici un exemple:
Vous avez besoin de prendre en compte la durée de vie de votre
*const c_char
pointeurs et qui les possède. Selon l'API C, vous pouvez avoir besoin de convoquer un groupe spécial de la fonction de libération sur la chaîne. Vous devez soigneusement organiser des conversions les tranches de ne pas survivre à l'pointeur. Le fait queCStr::from_ptr
renvoie une&CStr
arbitraire de la durée de vie utile ici (même si c'est dangereux par lui-même); par exemple, vous pouvez encapsuler votre chaîne C dans une structure et de fournir uneDeref
de conversion de sorte que vous pouvez utiliser votre struct comme si c'était une chaîne de tranche:Il y a aussi un autre type dans ce module, appelé
CString
. Il a la même relation avecCStr
commeString
avecstr
-CString
est de posséder une version deCStr
. Cela signifie qu'il "tient" la poignée de l'affectation de l'octet de données, et en les déposantCString
permettrait de libérer la mémoire qu'il fournit (essentiellement,CString
enveloppementsVec<u8>
, et c'est cette dernière qui sera supprimée). Par conséquent, il est utile lorsque vous souhaitez exposer les données allouées à la Rouille comme une chaîne C.Malheureusement, chaînes C toujours fin avec le zéro octet et ne peut pas contenir un à l'intérieur, tandis que la Rouille
&[u8]
/Vec<u8>
sont exactement à l'opposé chose - qu'ils ne finissent pas avec de zéro octet et peut contenir un nombre arbitraire de l'intérieur. Cela signifie que passer deVec<u8>
àCString
est ni exempt d'erreur, ni de l'allocation-gratuit - duCString
constructeur de deux chèques pour les zéros à l'intérieur les données que vous fournissez, de retourner une erreur si il en trouve, et ajoute un zéro octet à la fin de l'octet vecteur, qui peut exiger de sa réaffectation.Comme
String
, qui met en œuvreDeref<Target = str>
,CString
implémenteDeref<Target = CStr>
, de sorte que vous pouvez appeler les méthodes définies surCStr
directement surCString
. Ceci est important parce que laas_ptr()
méthode qui retourne la*const c_char
nécessaire pour C interopérabilité est définie surCStr
. Vous pouvez appeler cette méthode directement surCString
valeurs, ce qui est très pratique.CString
peut être créé à partir de tout ce qui peut être transformée enVec<u8>
.String
,&str
,Vec<u8>
et&[u8]
sont des arguments valables pour la fonction de constructeur,CString::new()
. Naturellement, si vous passez un octet tranche ou d'une chaîne de tranche, une nouvelle allocation va être créé, alors queVec<u8>
ouString
seront consommés.Si vous avez besoin d'un transfert de propriété de la
CString
de code en C, vous pouvez appelerCString::into_raw
. Vous devez ensuite obtenir le pointeur de retour et de les libérer dans la Rouille, la Rouille de l'allocateur est peu probable d'être le même que l'allocateur utilisé parmalloc
etfree
. Tout ce que vous devez faire est d'appelerCString::from_raw
et ensuite permettre à la chaîne d'être tombé normalement.En plus de ce que @vladimir-matveev a dit, vous pouvez également convertir des entre eux sans l'aide de
CStr
ouCString
:Assurez-vous juste que lors de la conversion à partir d'un &str pour une chaîne C, de votre &str se termine avec
'\0'
.Notez que dans le code ci-dessus-je utiliser
strlen(c_s)+1
au lieu destrlen(c_s)
, doncs
est"Hello World!\0"
, pas seulement"Hello World!"
.(Bien sûr, dans ce cas particulier, il fonctionne même avec juste
strlen(c_s)
. Mais avec une nouvelle &str vous ne pouvez pas garantir que la chaîne C termine là où l'on attend.)Voici le résultat de l'exécution du code:
CStr
, mais en évitant qu'il n'a pas de raison. Votre conversion est de retour est incorrect comme la Rouille&str
n'est pas NUL terminée, ce n'est pas valide C de la chaîne.s.as_ptr()
. Pour rendre cela plus clair, j'ai maintenant corrigéstrlen(c_s)
àstrlen(c_s)+1
.