2017-03-02 71 views
3

我想在Rust程序中使用命令行參數並將它們傳遞給C函數。但是,這些參數是可選的,如果沒有參數提供,程序應該有不同的表現。我已閱讀CString::as_ptr的文檔,但我希望保留一個包含參數Option的本地變量(如果存在的話)將保持String不被釋放,如下例所示。是否有更自然的方式來保持可選參數字符串被釋放?

此拉斯特代碼:

extern crate libc; 

use std::ffi::CString; 

extern "C" { 
    fn print_in_c(opt_class: *const libc::c_char) -> libc::c_int; 
} 

fn main() { 
    let mut args = std::env::args(); 
    //skip execuatble name 
    args.next(); 

    let possible_arg = args.next(); 

    println!("{:?}", possible_arg); 

    let arg_ptr = match possible_arg { 
     Some(arg) => CString::new(arg).unwrap().as_ptr(), 

     None => std::ptr::null(), 
    }; 

    unsafe { 
     print_in_c(arg_ptr); 
    }; 
} 

隨着這款C代碼:

#include <stdio.h> 
int 
print_in_c(const char *bar) 
{ 
    puts("C:"); 
    puts(bar); 

    return 0; 
} 

但這並沒有工作。 代碼打印出當通過「foo」的的參數如下:接着是空白行

Some("foo") 
C: 

我得到了程序打印正確的文本,如果我鏽代碼更改爲以下:

extern crate libc; 

use std::ffi::CString; 

extern "C" { 
    fn print_in_c(opt_class: *const libc::c_char) -> libc::c_int; 
} 

fn main() { 
    let mut args = std::env::args(); 
    //skip execuatble name 
    args.next(); 

    let possible_arg = args.next(); 

    println!("{:?}", possible_arg); 

    let mut might_be_necessary = CString::new("").unwrap(); 

    let arg_ptr = match possible_arg { 
     Some(arg) => { 
      might_be_necessary = CString::new(arg).unwrap(); 
      might_be_necessary.as_ptr() 
     } 

     None => std::ptr::null(), 
    }; 

    unsafe { 
     print_in_c(arg_ptr); 
    }; 
} 

運行時,該打印

Some("foo") 
C: 
foo 

預期。

這種方法在技術上的工作,但它是尷尬延伸到多個參數,並導致編譯器警告:

warning: value assigned to `might_be_necessary` is never read 
    --> src/main.rs:19:9 
    | 
19 |  let mut might_be_necessary = CString::new("").unwrap(); 
    |   ^^^^^^^^^^^^^^^^^^^^^^ 
    | 
    = note: #[warn(unused_assignments)] on by default 

有沒有更好的方式來做到這一點?

回答

7

問題是,您的代碼正在創建一個臨時的CString,但只保留一個指針。實際的CString被丟棄,而懸掛的指針被傳遞給C函數。通過引用,其壽命由編譯器仔細跟蹤

let arg_ptr = match possible_arg { 
    Some(arg) => { 
     let tmp = CString::new(arg).unwrap(); 
     tmp.as_ptr() 
    } // <-- tmp gets destructed here, arg_ptr is dangling 
    None => std::ptr::null(), 
}; 

安全鏽防止懸擺指針只支持間接指針:要了解發生了什麼事情,給模式匹配擴展到更詳細的形式是非常有用。在編譯時,任何超過對象的引用的使用都將被自動拒絕。但是您正在使用原始指針和一個阻止進行檢查的unsafe塊,因此您需要手動確保適當的生命週期。事實上,第二個代碼片段通過創建一個局部變量來修復問題,該局部變量足以存儲足夠長的值以使其值超出指針。

延長的使用壽命以一個額外的局部變量爲代價。但幸運的是可以避免的 - 因爲你已經保存指針的局部變量,你可以修改存儲實際CString,只提取指針時實際需要:

let arg_cstring = possible_arg.map(|arg| CString::new(arg).unwrap()); 
unsafe { 
    print_in_c(arg_cstring.as_ref() 
       .map(|cs| cs.as_ptr()) 
       .unwrap_or(std::ptr::null())); 
} 

有幾件事情注意到這裏:

  • arg_cstringOption<CString>,其確保具有CString存儲可以活得比傳遞給C函數指針;
  • Option::as_ref()用於防止arg_cstring移動到map,它將在實際使用指針之前再次釋放它;
  • Option::map()被用作模式匹配的替代方法,當您想要表達「如果如果Some,請執行某些操作,否則將其保留爲None」。
  • 模式x.as_ref().map(|x| x.as_ptr().unwrap_or(null())如果在程序中多次使用,可以並且可能應該將其移入實用函數。請注意該函數參考Option以避免移動。
相關問題