2017-03-02 72 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以避免移动。
相关问题