Вы создаете CString
в том же операторе, что и указатель на него. CString
принадлежит, но не привязан к переменной, поэтому он живет только в течение оператора, в результате чего указатель становится недействительным. Об этом специально предупреждает документация для as_ptr
:
Например, следующий код вызовет неопределенное поведение при использовании ptr внутри небезопасного блока:
use std::ffi::{CString};
let ptr = CString::new("Hello").expect("CString::new failed").as_ptr();
unsafe {
// `ptr` is dangling
*ptr;
}
Это происходит потому, что указатель, возвращаемый as_ptr
, не содержит никакой информации о времени жизни, а CString
освобождается сразу после вычисления выражения CString::new("Hello").expect("CString::new failed").as_ptr()
.
Вы можете решить проблему, введя переменные, которые будут жить для всей функции, а затем создать указатели на эти переменные:
fn main() {
let owned_test = CString::new("Hello World").unwrap();
let _test_str: *const c_char = owned_test.as_ptr();
let owned_fmt = CString::new("%s\n").unwrap();
let fmt: *const c_char = owned_fmt.as_ptr();
unsafe {
libc::printf(fmt, _test_str);
}
unsafe {
let slice = CStr::from_ptr(_test_str);
println!(
"string buffer size without nul terminator: {}",
slice.to_bytes().len()
);
}
// owned_fmt is dropped here, making fmt invalid
// owned_test is dropped here, making _test_str invalid
}
Если вы работаете с необработанными указателями, вам нужно быть особенно осторожным, чтобы они всегда указывали на текущие данные. Введение переменной - лучший способ точно определить, как долго эти данные будут жить - они будут жить от инициализации переменной до момента, когда переменная выходит из области видимости.