我想在Rust程序中获取命令行参数并将它们传递给C函数。但是,这些参数是可选的,如果没有提供参数,程序应该表现不同。我已经阅读了CString::as_ptr
的文档,但我希望保留一个包含Option
的局部变量包含参数(如果存在)将使String
不被释放,因为以下示例。
这个Rust代码:
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;
}
但这并没有奏效。 传递&#34; foo&#34;:
的参数时,代码会输出以下内容Some("foo")
C:
后面跟一个空行。
如果我将Rust代码更改为以下内容,我得到了打印正确文本的程序:
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
有更好的方法吗?
答案 0 :(得分:7)
问题是你的代码正在创建一个临时的 string str = ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:/Users/charlyn_dale/Documents/Visual Studio 2010/Projects/LMS/WindowsFormsApplication2/Accounts.accdb;Persist Security Info=False");
OleDbCommand conn = new OleDbCommand(str);
con.Open();
string query = "insert into Account ([Username],[Password],FirstName,MiddleName,LastName,Age,Section,Gender,Address,AccountStatus) values('" + txt1.Text + "','" + txt2.Text + "','" + txt4.Text + "','" + txt5.Text + "','" + txt6.Text + "','" + txt7.Text + "','" + txt8.Text + "','" + cmb2.Text + "','" + txt9.Text + "','" + cmb1.Text + "')";
OleDbCommand cmd = new OleDbCommand(query, con);
conn.ExecuteNonQuery();
MessageBox.Show("Registration Success!");
con.Close();
,但只保留一个指针。实际的CString
被删除,而悬空指针被传递给C函数。要了解发生了什么,将模式匹配扩展为更详细的形式非常有用:
CString
Safe Rust通过仅通过引用支持指针间接来防止悬空指针,引用的生命周期由编译器仔细跟踪。在编译时会自动拒绝使用超过对象的引用。但是您使用原始指针和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
,并仅在实际需要时提取指针:
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_cstring
,可确保Option<CString>
的存储空间可以超过传递给C函数的指针; CString
用于阻止Option::as_ref()
移动进入arg_cstring
,这将在指针实际使用之前再次释放它; map
用于替代模式匹配,如果您想表达“Option::map()
如果Option
执行某些操作,则只需将其保留为Some
”。< / LI>
None
模式可以并且可能应该移动到实用程序函数中。请注意,该函数会引用x.as_ref().map(|x| x.as_ptr().unwrap_or(null())
以避免移动。