我试图在Rust中编写readline自定义完成符(制表符完成)。我想我的一切都很直,但当我尝试体内时,它会进入杂草而永远不会回来。奇怪的是,当我直接从main()
调用它时,我似乎得到了一个有效的指针。在任何一种情况下,我都不会看到崩溃或恐慌。 Backtrace输出在运行时不一致(它忙于做某事)。也许有一个线索是gdb表明传递给完成者的参数是不正确的(尽管我实际上并没有使用它们)。例如,在回调之后:
#2 0x00007f141f701272 in readlinetest::complete (text=0x7f141ff27d10 "", start=2704437, end=499122176) at src/main.rs:24
或者直接打破main
中的来电:
#0 readlinetest::complete (text=0x555555559190 <complete::hcda8d6cb2ef52a1bKaa> "dH;$%p", start=0, end=0) at src/main.rs:21
我有ABI问题吗?似乎不太可能,功能签名并不复杂:(
这是一个小型测试项目:Cargo.toml
:
[package]
name = "readlinetest"
version = "0.1.0"
authors = ["You <you@example.com>"]
[dependencies]
libc = "*"
readline = "*"
main.rs
:
extern crate libc;
extern crate readline;
use libc::{c_char, c_int};
use std::ffi::CString;
use std::process::exit;
use std::ptr;
use std::str;
extern { fn puts(s: *const libc::c_char); }
#[link(name = "readline")]
// Define the global in libreadline that will point to our completion function.
extern {
static mut rl_completion_entry_function: extern fn(text: *const c_char,
start: c_int,
end: c_int) -> *const *const c_char;
}
// Our completion function. Returns two strings.
extern fn complete(text: *const c_char, start: c_int, end: c_int) -> *const *const c_char {
let _ = text; let _ = start; let _ = end;
let mut words:Vec<*const c_char> =
vec!(CString::new("one").unwrap(), CString::new("two").unwrap()).
iter().
map(|arg| arg.as_ptr()).
collect();
words.push(ptr::null()); // append null
words.as_ptr() as *const *const c_char
}
fn main() {
let words = complete(string_to_mut_c_char("hi"), 1, 2);
unsafe { puts(*words) } // prints "one"
//unsafe { puts((*words + ?)) } // not sure hot to get to "two"
unsafe { rl_completion_entry_function = complete }
// Loop until EOF: echo input to stdout
loop {
if let Ok(input) = readline::readline_bare(&CString::new("> ").unwrap()) {
let text = str::from_utf8(&input.to_bytes()).unwrap();
println!("{}", text);
} else { // EOF/^D
exit(0)
}
}
}
// Just for testing
fn string_to_mut_c_char(s: &str) -> *mut c_char {
let mut bytes = s.to_string().into_bytes(); // Vec<u8>
bytes.push(0); // terminating null
let mut cchars = bytes.iter().map(|b| *b as c_char).collect::<Vec<c_char>>();
let name: *mut c_char = cchars.as_mut_ptr();
name
}
Ubuntu 14.04,64 bit with Rust 1.3。
我错过了什么?感谢任何指针(哈哈......)。
答案 0 :(得分:3)
并且函数签名不复杂
不是,但它确实有合适的... ^ _ ^来自我当地版本的readline(6.3.8):
extern rl_compentry_func_t *rl_completion_entry_function;
typedef char *rl_compentry_func_t PARAMS((const char *, int));
此外,您还有多个在免费错误后使用:
vec![CString::new("one").unwrap()].iter().map(|s| s.as_ptr());
这会创建一个CString
并获取指向它的指针。语句完成后,没有拥有拥有字符串的向量。向量将立即被删除,丢弃字符串,使指针无效。
words.as_ptr() as *const *const c_char
这里类似的东西 - 你拿指针,然后没有任何东西 words
向量,所以它被删除,使指针无效。所以现在你有一个无效指针,试图指向一系列无效指针。
string_to_mut_c_char
可以找到同样的问题。
我不知道足够的readline来理解谁应该拥有返回的字符串,但看起来像你将所有权传递给readline并且它释放了它们。如果是这样,那意味着你将不得不使用readline所做的相同分配器,以便它可以为你释放字符串。您可能必须编写一些使用适当的分配器复制CString数据的自定义代码。
样式方面,您可以在变量名中使用下划线表示它们未被使用:
extern fn complete(_text: *const c_char, _start: c_int, _end: c_int)
在:
之后应该有一个空格,并且不需要指定向量内容的类型:
let mut words: Vec<_>