Open4

Rustのcore-foundation crateで*const c_voidになってしまったオブジェクトを元の型に戻す

Shotaro TsujiShotaro Tsuji

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トレイトの使い方を理解するのに少し時間がかかったのでメモをしておく。

Shotaro TsujiShotaro Tsuji

実装

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トレイトを実装していれば自動で実装される。

Shotaro TsujiShotaro Tsuji

他のクラスにキャストすると?

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側のランタイムエラーが発生してアボートする。
}