Open4
Rustのcore-foundation crateで*const c_voidになってしまったオブジェクトを元の型に戻す
tl;dr
FromVoidトレイトを使う。例えばCFString
に戻すのであれば次のようにすればよい。
fn c_void_to_cf_string<'a>(p: ItemRef<'a, *const c_void>) -> ItemRef<'a, CFString> {
assert!(!p.is_null());
let s = unsafe { CFString::from_void(*p) };
assert!(s.instance_of::<CFString>());
s
}
動機
APIを呼び出してCFDictionary
が返ってくると、辞書の値は*const c_void
になってしまっているので元の型を思い出す必要がある。FromVoid
トレイトの使い方を理解するのに少し時間がかかったのでメモをしておく。
実装
core_foundation::base
で実装されている。
base.rs
unsafe impl<T: TCFType> FromVoid for T {
unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> {
ItemRef(ManuallyDrop::new(TCFType::wrap_under_create_rule(T::Ref::from_void_ptr(x))), PhantomData)
}
}
FromVoid
トレイトはTCFType
トレイトを実装していれば自動で実装される。
TCFType::wrap_under_create_rule
この関連関数は参照カウンタを増やさずにポインタをRustの構造体でラップする。ラップする構造体にはDrop
トレイトが実装されていて、ドロップされる時にCFRelease
が呼び出されるようになっている。
他のクラスにキャストすると?
from_void
を使うことで異なるクラスにキャストすることができる。from_void
はポインタをラップするだけなので、これが呼び出された時点ではおかしなことは起こらない。しかし、メソッドの呼び出しを行うと存在しないメソッドを呼び出すことになりアボートする。
以下ではCFString
のオブジェクトをCFNumber
のラッパー構造体に変換してからCFNumberGetValue
を呼び出している。
fn cast_number_to_string() {
let s = CFString::from_static_string("Hello");
let p = s.to_void();
let n = unsafe { CFNumber::from_void(p) }; // この時点では何も起こらない。
let _x = n.to_f32(); // Core Foundation側のランタイムエラーが発生してアボートする。
}