Cライブラリから返されたC文字列を取得し、FFIを介してRust文字列に変換しようとしています。
mylib.c
const char* hello(){
return "Hello World!";
}
main.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?
}
RustでC文字列を操作する最良の方法は、 _std::ffi
_ モジュールの構造を使用することです。すなわち、 CStr
および CString
。
CStr
は動的なサイズの型であるため、ポインターを介してのみ使用できます。これにより、通常のstr
型と非常によく似ています。安全でない _&CStr
_ 静的メソッドを使用して、_*const c_char
_から_CStr::from_ptr
_を構築できます。このメソッドは、渡される生のポインターが有効であるという保証がなく、有効なC文字列を実際に指し示し、文字列の有効期間が正しいという保証がないため、安全ではありません。
_&str
_は、その to_str()
メソッドを使用して_&CStr
_から取得できます。
以下に例を示します。
_extern crate libc;
use libc::c_char;
use std::ffi::CStr;
use std::str;
extern {
fn hello() -> *const c_char;
}
fn main() {
let c_buf: *const c_char = unsafe { hello() };
let c_str: &CStr = unsafe { CStr::from_ptr(c_buf) };
let str_slice: &str = c_str.to_str().unwrap();
let str_buf: String = str_slice.to_owned(); // if necessary
}
_
_*const c_char
_ポインターの寿命とそれらの所有者を考慮する必要があります。 C APIによっては、文字列で特別な割り当て解除関数を呼び出す必要がある場合があります。スライスがポインタよりも長持ちしないように、変換を慎重に調整する必要があります。 _CStr::from_ptr
_が任意の有効期間で_&CStr
_を返すという事実は、ここで役立ちます(それ自体は危険です)。たとえば、C文字列を構造体にカプセル化し、Deref
変換を提供して、構造体を文字列スライスであるかのように使用できます。
_extern crate libc;
use libc::c_char;
use std::ops::Deref;
use std::ffi::CStr;
extern "C" {
fn hello() -> *const c_char;
fn goodbye(s: *const c_char);
}
struct Greeting {
message: *const c_char,
}
impl Drop for Greeting {
fn drop(&mut self) {
unsafe {
goodbye(self.message);
}
}
}
impl Greeting {
fn new() -> Greeting {
Greeting { message: unsafe { hello() } }
}
}
impl Deref for Greeting {
type Target = str;
fn deref<'a>(&'a self) -> &'a str {
let c_str = unsafe { CStr::from_ptr(self.message) };
c_str.to_str().unwrap()
}
}
_
このモジュールには、 CString
と呼ばれる別のタイプもあります。 CStr
との関係は、String
のstr
と同じです。CString
は、CStr
の所有バージョンです。つまり、バイトデータの割り当てへのハンドルを「保持」し、CString
をドロップすると、提供するメモリが解放されます(基本的に、CString
は_Vec<u8>
_をラップし、後者がドロップされます)。したがって、Rustに割り当てられたデータをC文字列として公開する場合に役立ちます。
残念ながら、Cの文字列は常にゼロバイトで終わり、その中に1つを含めることはできませんが、Rust _&[u8]
_/_Vec<u8>
_は正反対です-それらは含まれませんこれは、_Vec<u8>
_からCString
への移行はエラーフリーでも割り当てフリーでもないことを意味します-CString
コンストラクターは、指定されたデータ内のゼロをチェックし、エラーが見つかった場合、エラーが発生し、バイトベクトルの最後にゼロバイトを追加します。
_Deref<Target = str>
_を実装するString
と同様に、CString
は_Deref<Target = CStr>
_を実装するため、CStr
で定義されたメソッドをCString
で直接呼び出すことができます。 Cの相互運用に必要な_*const c_char
_を返す as_ptr()
メソッドがCStr
で定義されているため、これは重要です。このメソッドはCString
値で直接呼び出すことができ、便利です。
CString
は、_Vec<u8>
_に変換できるすべてのものから作成できます。 String
、_&str
_、_Vec<u8>
_、および_&[u8]
_は、コンストラクター関数の有効な引数です CString::new()
。当然、バイトスライスまたは文字列スライスを渡すと、新しい割り当てが作成され、_Vec<u8>
_またはString
が消費されます。
_extern crate libc;
use libc::c_char;
use std::ffi::CString;
fn main() {
let c_str_1 = CString::new("hello").unwrap(); // from a &str, creates a new allocation
let c_str_2 = CString::new(b"world" as &[u8]).unwrap(); // from a &[u8], creates a new allocation
let data: Vec<u8> = b"12345678".to_vec(); // from a Vec<u8>, consumes it
let c_str_3 = CString::new(data).unwrap();
// and now you can obtain a pointer to a valid zero-terminated string
// make sure you don't use it after c_str_2 is dropped
let c_ptr: *const c_char = c_str_2.as_ptr();
// the following will print an error message because the source data
// contains zero bytes
let data: Vec<u8> = vec![1, 2, 3, 0, 4, 5, 0, 6];
match CString::new(data) {
Ok(c_str_4) => println!("Got a C string: {:p}", c_str_4.as_ptr()),
Err(e) => println!("Error getting a C string: {}", e),
}
}
_
CString
の所有権をCコードに転送する必要がある場合は、 _CString::into_raw
_ を呼び出すことができます。その後、ポインターを取得してRustで解放する必要があります。 Rustアロケーターは、malloc
およびfree
が使用するアロケーターと同じである可能性は低いです。必要なのは、 _CString::from_raw
_ その後、文字列を通常どおりドロップします。