为何`windows` crate中的COM接口类型不具备`Send`特性?
windows crate中的COM接口类型不具备Send特性? 咱们先理清楚这里的核心矛盾:你知道COM对象有线程模型,跨线程调用会自动走代理,但疑惑为什么Rust的windows crate里,连最基础的windows_core::IUnknown都标记为!Send,所有COM接口也跟着继承这个特性。
其实核心原因在于Rust的静态类型安全设计,和COM线程模型的动态性之间的不匹配,具体来说有这几点:
默认保守的安全策略:不是所有COM对象都能安全地在线程间转移所有权。虽然大部分COM对象跨线程调用时会通过代理正常工作,但存在一些特殊实现——比如某些自定义STA对象内部依赖创建线程的线程本地存储(TLS)、线程专属的资源句柄等。如果直接把这类对象的所有权移到另一个线程,哪怕调用会被代理,对象内部的一些操作也可能因为脱离了原线程环境而触发未定义行为。为了避免用户在不知情的情况下踩坑,
windowscrate选择默认不标记Send,把安全判断的主动权交给开发者。静态特性 vs 动态线程模型:Rust的
Send是编译时的静态特性标记,而COM对象的线程模型是运行时动态确定的——你只有在运行时才能知道一个对象是属于单线程公寓(STA)还是多线程公寓(MTA)。windowscrate没办法在编译时预判你使用的COM对象是否支持跨线程移动,所以只能采用最安全的默认策略,不赋予Send特性。显式安全的选择权在你:如果你明确知道某个COM对象可以安全跨线程移动(比如MTA创建的对象,或者你确认该STA对象的所有操作都能被正确封送),你可以通过
unsafe impl Send for 你的COM接口类型手动添加Send特性,但这一步需要你自己承担安全责任,因为编译器没办法帮你验证这个对象的实际线程兼容性。
备注:内容来源于stack exchange,提问作者laptou

