[解決済み] FFIを使用してCの文字列をRustの文字列に変換する方法は?
質問
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言語の文字列を扱う最良の方法は、C言語の文字列を扱うための構造体である
std::ffi
モジュール、すなわち
CStr
と
CString
.
CStr
は動的な大きさの型なので、ポインタを通してのみ使用することができます。このため、通常の
str
型と非常によく似ています。を構築することができます。
&CStr
から
*const c_char
を使用して、安全でない
CStr::from_ptr
静的メソッドを使用しています。このメソッドは安全ではありません。なぜなら、このメソッドに渡される生のポインタが有効であること、それが本当に有効なC文字列を指していること、そして文字列の寿命が正しいことは保証されないからです。
を取得することができます。
&str
から
&CStr
を使って、その
to_str()
メソッドを使用します。
以下はその例です。
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言語の文字列は常に0バイトで終わり、その中に1バイトを含むことはできませんが、Rustの
&[u8]
/
Vec<u8>
は全く逆で、0バイトで終わらず、内部に任意の数の0バイトを含むことができます。これはつまり
Vec<u8>
から
CString
はエラーフリーでもアロケーションフリーでもありません。
CString
コンストラクタは、提供されたデータ内にゼロがあるかどうかをチェックし、見つかった場合はエラーを返し、バイトベクタの末尾にゼロバイトを追加します。
例えば
String
を実装している
Deref<Target = str>
,
CString
実装
Deref<Target = CStr>
で定義されたメソッドを呼び出すことができます。
CStr
で定義されたメソッドを直接
CString
. これは重要です。
as_ptr()
を返すメソッドは
*const c_char
は、C言語の相互運用に必要な
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 のアロケータが同じであることはまずありません。
malloc
と
free
. 必要なのは
CString::from_raw
と入力し、文字列が普通に落とせるようにすればよいのです。
関連
-
g++が内部・外部コマンドソリューションとして認識されない、MinGWを初めて使うときの落とし穴
-
libc++abi.dylib: NSException タイプの捕捉されない例外で終了するエラー
-
C 言語のポインタ配列のポインタ型、ポインタに値を割り当てるために配列名を使用、コンパイル時の警告:互換性のないポインタ型からの初期化
-
[解決済み] mallocで文字列を確保する
-
[解決済み] Windows用Cコンパイラ?[クローズド]
-
[解決済み] "static const" vs "#define" vs "enum"
-
[解決済み] 1ビットのセット、クリア、トグルはどのように行うのですか?
-
[解決済み] C言語で配列のサイズを決定するにはどうすればよいですか?
-
[解決済み] プログラム終了前にmallocの後にfreeをしないと本当に何が起こるのか?
-
[解決済み] なぜsizeof(x++)はxをインクリメントしないのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
_CRT_SECURE_NO_WARNINGS エラーメッセージ、解決方法
-
構造体の配列--[エラー] '['トークンの前に一次式があることが予想される
-
[C] レポートエラー 代入の左オペランドとしてlvalueが必要
-
[解決済み] Valgrind が初期化されていないバイトについて警告する
-
[解決済み] SQLiteのINSERT/per-secondのパフォーマンスを向上させる
-
[解決済み] C言語のコードで「:-!」とは何ですか?
-
[解決済み] C言語で関数をパラメータとして渡すにはどうすればよいですか?
-
[解決済み] char s[]とchar *sの違いは何ですか?
-
[解決済み] なぜ16進数には0xがつくのですか?
-
[解決済み] FortranはC言語よりも重い計算を最適化しやすいですか?