2017-03-10 14 views
0

私は、libcライブラリに直接リンクするRustのシェルを書こうとしていました。 Vec<String>を使用して、引数をexecvp()に渡しましたが、char **への変換が成功していないようです。実行時に、すべてのパラメータがNULL文字列になりました。RustのVec <String>をCのC ** **に転送する

ここにコードがあります。

fn safe_execvp(path: String, argv: Vec<String>) -> Result<(), i32> { 
    unsafe { 
     let c_path = CString::new(path.as_str()).unwrap(); 
     let mut c_argv_vec = Vec::new(); 
     for arg in &argv { 
      let c_arg = CString::new(arg.as_str()).unwrap().as_ptr(); 
      c_argv_vec.push(c_arg); 
     } 
     c_argv_vec.push(std::ptr::null()); 
     match execvp(c_file.as_ptr(), c_argv_vec.as_ptr()) { 
      num => Err(num), 
     } 
    } 
} 

execvpfn execvp(file: *const i8, argv: *const*const i8) -> i32;として定義されるCライブラリ関数です。

私は何が間違っているのか分かりません。それは、引数のメモリがexecvp()にコールする前に解放されたからでしょうか?

答えて

5

CStringインスタンスを作成していて、すぐにポインタを取得しています。したがって、あなたが推測しているように、この文字列の所有権は早期に失われました。ポインタがライフタイム情報を保持しないため、この場合はコンパイルエラーが発生しない点を除いて、ローカルインスタンスへの参照を返す場合と似ています。

問題の解決策は、関数のスコープ中に所有されているCスタイルの文字列を保持し、同じ内容のポインタを別々に指すポインタを生成することです。

let cstr_argv: Vec<_> = argv.iter() 
     .map(|arg| CString::new(arg.as_str()).unwrap()) 
     .collect(); 

let mut p_argv: Vec<_> = cstr_argv.iter() // do NOT into_iter() 
     .map(|arg| arg.as_ptr()) 
     .collect(); 

p_argv.push(std::ptr::null()); 

let p: *const *const c_char = p_argv.as_ptr(); 

Playground

+0

ありがとう!私は 'CString'をベクトルに保つべきだったと思います。 –

4

私はあなたのCString::as_ptragainのドキュメントを読むことをお勧め:

WARNING

根本的なメモリが 早く過ぎる解放されていないことを確認するあなたの責任です。例えば、PTRが危険なブロックの内部で使用されている場合、次のコードは、 未定義の動作が発生します。

use std::ffi::{CString}; 

let ptr = CString::new("Hello").unwrap().as_ptr(); 
unsafe { 
    // `ptr` is dangling 
    *ptr; 
} 

あなたはドキュメントが何をしていないと言う正確に何をしています。

+0

ありがとうございます!私はその部分を読んだが、私はそれを警告しなかった間違いをしたことに気付かなかった。 –

関連する問題