Проверка заимствования для поля структуры C

Представьте, что C API предоставляет непрозрачный указатель на некоторые данные и два средства доступа к некоторому полю void set_string(struct foo*, const char*) и const char* get_string(struct foo*), и в этой документации указано что-то вроде:

Строка, возвращаемая get_string, действительна до тех пор, пока действителен непрозрачный указатель на foo и не выполняется последующий вызов set_string. В противном случае поведение не определено

Это простой пример заголовка из C, который иллюстрирует проблему.

//foo.h
struct foo;
const char* get_string(struct foo*);
void set_string(struct foo*, const char*);

Мне интересно, можно ли и как заставить средство проверки заимствований ржавчины следить за этой внешней ссылкой и избегать потенциальных UB после создания привязок с помощьюbindgen. Я экспериментировал с

use foo::foo;
use std::marker::PhantomData;

struct Foo<'a> {
    pointer: *const foo,
    _phantom: PhantomData<&'a CStr>,
}

но, похоже, это тупик.

Вы можете обернуть unsafe вызовы extern функций C в безопасный API, который вы предоставляете своему коду Rust: пример.

eggyal 07.07.2024 21:38
struct foo* в C эквивалентен *mut foo в Rust, а не *const foo
cafce25 07.07.2024 22:06

Единственное беспокойство заключается в том, что вы не предоставили документацию, подтверждающую право собственности на const char*, переданный set_string. Это просто «ссылка», и она выделяет копию внутри себя? Или функция «претендует на владение» строковыми данными? Если да, то как это освобождает его? Будем надеяться, что в большей части документации об этом говорится, поскольку это повлияет на то, как именно обеспечить этот безопасный интерфейс, но это в значительной степени имеет отношение к тому, как обрабатывать время жизни.

kmdreko 07.07.2024 22:27
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
3
138
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Таким образом, документация, по сути, говорит вам, что значение, возвращаемое get_string, заимствовано из struct foo, а set_string мутирует его, поэтому требуется изменяемое заимствование, вы можете сделать это без какого-либо времени жизни в struct Foo на стороне Rust, просто оберните вызовы set|get_string в безопасные абстрагирующие методы :

// creating a module to contain the code that needs to be checked for soundness
mod foo_mod {
    use std::ffi::{c_char, CString, CStr};
    struct foo;
    pub struct Foo {
        // musn't be `pub`
        pointer: *mut foo,
    }
    impl Foo {
        pub fn set_string(&mut self, s: CString) {
            extern "C" {
                fn set_string(this: *mut foo, s: *const c_char);
            }
            // SAFETY:
            // this is safe assuming:
            // - `self.pointer` is always a valid pointer to a `struct foo`
            // - `set_string` in C does not deallocate `s` (unless by leveraging the appropriate Rust code to do so)
            unsafe { set_string(self.pointer, s.into_raw()) }
        }
        pub fn get_string(&self) -> &CStr {
            extern "C" {
                fn get_string(this: *mut foo) -> *const c_char;
            }
            // SAFETY:
            // this is safe assuming:
            // - `self.pointer` is always a valid pointer to a `struct foo`
            // - `char* get_string()` always returns a valid C string given a valid `struct foo` pointer
            unsafe {
                let ptr = get_string(self.pointer);
                CStr::from_ptr(ptr)
            }
        }
        pub fn new() -> Self {
            // this or any constructors must make sure to only create `Foo`s that contain a valid `pointer`
            todo!()
        }
    }
}

Детская площадка


Примечание: Foo::set_string в Rust безопасен согласно изложенным предположениям, но он пропускает переданное CString каждый раз, когда вы его используете. В производственной среде это вряд ли то, что вам нужно, но правильная работа с этим зависит от того, как Cs set_string ожидает, что строка будет передана как kmdreko примечания.

Другие вопросы по теме