Как напечатать переменную частного типа, которая не поддерживает отображение или отладку?

Представьте, что у нас есть частная библиотека, в которую мы не можем вносить изменения и которая содержит некоторый тип, не представленный в библиотечном модуле.

// lib.rs
mod inner {
    pub struct SecretData {
        value: u32,
    }

    impl SecretData {
        fn new(value: u32) -> Self {
            SecretData { value }
        }
    }

    pub fn create_secret() -> SecretData {
        SecretData::new(42)
    }
}

pub use inner::create_secret;

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

// main.rs
use my_crate::create_secret;


fn main() {
    let secret = create_secret();
    println!("The secret is: {:?}", secret);
}

Приведенный выше код в основной функции приведет к ошибке, указывающей, что признак Debug не реализован для структуры SecretData.

Есть ли способ распечатать значение secret?

Невозможно напечатать SecretData (непрозрачную структуру) без ее поддержки или возникновения UB.

cafce25 13.07.2024 11:01

Ну, вы можете прочитать необработанные байты, которые он занимает в памяти, но нет никакого способа их интерпретировать (например, чтобы вернуть содержащееся значение).

eggyal 13.07.2024 11:08

@eggyal, это же UB.

cafce25 13.07.2024 11:13

Наверное, потому что неизвестно, все ли байты инициализированы? Справедливо.

eggyal 13.07.2024 11:16

Да, макет не указан и может содержать отступы. Для оболочки newtype вокруг u32 это может быть нормально, но это не гарантировано, и я предполагаю, что это просто упрощенная версия, а фактическая SecretData более сложная.

cafce25 13.07.2024 11:55
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
5
98
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Это невозможно в общем.

Чтобы иметь возможность печатать значимые данные из структуры, вам необходимо:

  1. либо у вас есть код, который печатает его для вас, либо преобразует его в печатное представление, то есть любой отображаемый признак из std::fmt , или собственный метод, такой как Path::display
  2. или узнайте расположение SecretData в памяти, чтобы вы могли безопасно прочитать и распечатать его самостоятельно. Это исключает все структуры repr(Rust).

Для структуры, расположение памяти которой вам известно, вы можете преобразовать в структуру, которая реализует черту отображения:

mod inner {
    #[repr(C)]
    pub struct SecretData {
        value: u32,
    }

    impl SecretData {
        fn new(value: u32) -> Self {
            SecretData { value }
        }
    }

    pub fn create_secret() -> SecretData {
        SecretData::new(42)
    }
}

use inner::create_secret;

#[repr(C)]
#[derive(Debug)]
struct NotSoSecret {
    v: u32,
}
fn main() {
    let secret = create_secret();
    // # Safety:
    // Both `SecretData` and `NotSoSecret` are `#[repr(C)]` structs with a single `u32` field so they both have the same memory layout.
    println!("The secret is: {:?}", unsafe {
        std::mem::transmute::<_, NotSoSecret>(secret)
    });
}

Примечание. Оба #[repr(C)] (или другой способ обеспечить одинаковую структуру памяти) обязательны, поскольку в противном случае структура памяти не указана, что делает transmute UB.

ОП описывает библиотеку как черный ящик, который является частной библиотекой, в которую мы не можем вносить изменения. Нет возможности изменить представление макета типа для этой библиотеки после компиляции.

Kaplan 13.07.2024 16:31

Да, это решается первым предложением: «Это невозможно».

cafce25 13.07.2024 16:33

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

Почему объекты, имеющие экземпляр класса с частной собственностью в прототипе, выбрасывают при доступе к частному члену?
Можно ли передать по значению объект с частным деструктором в функции?
Развертывание Flask Microblog в частном ec2 с частным RDS
У нас есть приложение с частной функцией. При развертывании функционального HTTP-триггера с использованием кода VS мы получаем ошибку 403
Редактировать закрытый тег с помощью Pydicom
Альтернативный конструктор копирования в С++
Извлечение изображений из частного репозитория в kubernetes без использования imagePullSecrets
Почему мы можем получить доступ к закрытым классам членов данных с помощью указателей, не используя функции друзей других членов класса?
Как использовать ctypes для вызова функции DLL с двойным подчеркиванием имени функции?
Почему я не могу получить доступ к защищенному методу из частного метода, если защищенные наследуются подклассами?