Во-первых, ваша реализация ToPtr
приглашает необоснованное кода. Воспроизводятся здесь:
// code in italics is wrong
impl ToPtr for str {
fn to_ptr(&self) -> *const i8 {
CString::new(self).unwrap().as_ptr()
}
}
Это выделяет новый CString
и возвращает указатель на его содержание, но CString
отбрасывается, когда to_ptr
возвращается, так что это оборванный указатель. Любое разыменование этого указателя является неопределенным поведением.documentation имеет большое предупреждение об этом, но это по-прежнему очень распространенная ошибка.
Один правильный способ сделать *const c_char
из строкового литерала - b"string here\0".as_ptr() as *const c_char
. Строка имеет нулевое завершение, и нет никакого висячего указателя, потому что строковые литералы живут для всей программы. Если у вас есть непостоянная строка для преобразования, вы должны держать CString
живы, пока он используется, например:
let s = "foo";
let cs = CString::new(s).unwrap(); // don't call .as_ptr(), so the CString stays alive
unsafe { some_c_function(cs.as_ptr()); }
// CString is dropped here, after we're done with it
Помимо: редактор «предложил» (я новый в Stack Overflow, но мне кажется, более вежлив комментировать, а не пытаться переписать мой ответ), что приведенный выше код может быть записан следующим образом:
let s = "foo";
unsafe {
// due to temporary drop rules, the CString will be dropped at the end of the statement (the `;`)
some_c_function(CString::new(s).unwrap().as_ptr());
}
Хотя это технически правильно (лучший вид правильно), «временные правила падения» - тонкие - это работает, потому что as_ptr
принимает ссылку на CString
(на самом деле & CStr, потому что компилятор изменяет метод цепочки в CString :: новый (s) .unwrap() deref() as_ptr().). вместо того, чтобы потреблять его, и потому что у нас есть только одна функция C для вызова. Я не люблю полагаться на что-то тонкое или неочевидное при написании небезопасного кода.
С этим из пути, я fixed that unsoundness в коде (вызовы все использовать строковые литералы, так что я просто использовал свою первую стратегию выше).Я получаю этот вывод по OSX:
0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 8, 1, 0, 0, 0, 0, 0, 0, 0
0, 0, 8, 2, 0, 0, 0, 0, 0, 0, 0
0, 0, 4, 3, 0, 0, 0, 0, 0, 0, 0
Итак, это соответствует вашим результатам, не так ли? Я также написал следующую программу C:
#include <stdio.h>
#include <unistd.h>
int main() {
struct __sFILE *fp1 = fdopen(STDIN_FILENO, "r");
struct __sFILE *fp2 = fdopen(STDOUT_FILENO, "w");
struct __sFILE *fp3 = fdopen(STDERR_FILENO, "w");
struct __sFILE *passwd = fopen("/etc/passwd", "r");
printf("%i %i %i %i\n", fp1->_flags, fp2->_flags, fp3->_flags, passwd->_flags);
}
И получил результат:
4 8 8 4
который, кажется, оправдает результаты Руст. Существует комментарий в верхней части /usr/include/stdio.h
, который говорит:
/*
* The following always hold:
*
* if (_flags&(__SLBF|__SWR)) == (__SLBF|__SWR),
* _lbfsize is -_bf._size, else _lbfsize is 0
* if _flags&__SRD, _w is 0
* if _flags&__SWR, _r is 0
*/
И глядя эти константы:
#define __SLBF 0x0001 /* line buffered */
#define __SRD 0x0004 /* OK to read */
#define __SWR 0x0008 /* OK to write */
Это, кажется, соответствует на выходе мы получаем: 4 для файла, открытого в режиме чтения, 8 для записи. Так в чем проблема?
Код, который вы отправили, не скомпилирован (путаница fp и passwd, я думаю). Кроме того, строки, которые вы передаете функции C, не завершены NUL, поэтому ваш код имеет полностью неопределенное поведение. –
О, да, я забыл добавить один файл, который завершает NUL, я обновил gist выше – hansaplast
Не беспокоился об утечке строки каждый раз? Во всяком случае, я думаю, что второй аргумент имеет ту же проблему. –