Представьте, что у нас есть частная библиотека, в которую мы не можем вносить изменения и которая содержит некоторый тип, не представленный в библиотечном модуле.
// 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
?
Ну, вы можете прочитать необработанные байты, которые он занимает в памяти, но нет никакого способа их интерпретировать (например, чтобы вернуть содержащееся значение).
@eggyal, это же UB.
Наверное, потому что неизвестно, все ли байты инициализированы? Справедливо.
Да, макет не указан и может содержать отступы. Для оболочки newtype вокруг u32
это может быть нормально, но это не гарантировано, и я предполагаю, что это просто упрощенная версия, а фактическая SecretData
более сложная.
Это невозможно в общем.
Чтобы иметь возможность печатать значимые данные из структуры, вам необходимо:
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.
ОП описывает библиотеку как черный ящик, который является частной библиотекой, в которую мы не можем вносить изменения. Нет возможности изменить представление макета типа для этой библиотеки после компиляции.
Да, это решается первым предложением: «Это невозможно».
Невозможно напечатать
SecretData
(непрозрачную структуру) без ее поддержки или возникновения UB.