Чтение необработанной строки C в Rust ... как правильно преобразовать подписанные в неподписанные в этом контексте? - PullRequest
1 голос
/ 28 сентября 2019

Я связываю некоторые функции C с ржавчиной.Я столкнулся с небольшой проблемой, и я хотел бы знать правильный способ ее решения в ржавчине.

Вот функция, которую я хотел бы вызвать из C API:

extern "C" {
    pub fn H5Aread(attr_id: hid_t, type_id: hid_t, buf: *mut c_char) -> herr_t;
}

Функция читает что-то из файла и сохраняет его в buf.

Итак, я создал этот буфер в векторе:

let len: u64 = get_the_length();

let attr_raw_string: Vec<c_char> = Vec::new(); // c_char is equivalent to i8
attr_raw_string.resize(len as usize, 0);
let attr_raw_string_ptr = attr_raw_string.as_mut_ptr();

let read_error = H5Aread(attr_obj, attr_type, attr_raw_string_ptr);
if read_error < 0 {
    panic!("...");
}
let result_str: String = String::from_utf8(attr_raw_string);

Теперь это не компилируется, потому что from_utf8 ожидает Vec<u8>, но Vec<c_char> - это Vec<i8>.

Есть ли способ исправить это без необходимости каждый раз копировать и приводить строку как новый тип u8?

1 Ответ

1 голос
/ 29 сентября 2019

Вы были почти там.

На данный момент мы будем предполагать, что сторона C вашей границы FFI правильная - то есть она правильно генерирует строку с нулевым символом в конце.

Чтобы эффективно назначить и восстановить это в ржавчине, мы будем использовать CStr.Это создает заимствованный тип, ссылающийся на строку C в памяти (т.е. *const char).Это не выделяет, так как это не принадлежащий тип.

Затем мы конвертируем это в &str для окончательного сравнения с тем, что мы ожидали.Это все еще не принадлежащий тип, поэтому все, что мы создали, это наш Vec<>, который мы эффективно использовали в качестве буфера.

Полный код доступен ниже и на площадке :

#[test]
fn test() {
    let len:u64 = 64;
    // Allocate a buffer
    let mut buffer:Vec<c_char> = Vec::with_capacity(len as usize);
    let attr_raw_string_ptr = buffer.as_mut_ptr();

    let read_error = unsafe { H5Aread(attr_raw_string_ptr) };
    if read_error < 0 {
        panic!("...");
    }
    let result = unsafe {
        CStr::from_ptr(attr_raw_string_ptr)
    };
    let result_str = result.to_str().unwrap();
    assert_eq!(result_str, "test");
}

Три важных ошибки:

  1. CStr::to_str() может завершиться ошибкой (следовательно, почему он возвращает Result<&str, _>, если содержимое CStrнедопустимо utf-8. Это потому, что оба типа rust String и &str должны быть действительными utf-8.
  2. Очевидно, ваш входной буфер должен быть как минимум размером с ваш CФункция даст сбой. Обратитесь к стороне C., чтобы получить эту гарантию.
  3. CStr::from_ptr имеет кучу ошибок , о которых вы должны по крайней мере помнить при использовании
...