【发布时间】:2020-12-17 06:55:04
【问题描述】:
我正在清除 Rust 中的依赖注入 (DI)。我有一些工作,但我正在努力使其更好。据我了解,Rust 确实不支持带有类型参数的裸约束(就像其他一些语言一样)。我正在尝试确定是否有可能以不安全的方式伪造裸约束(假设这是必要的)。对于不正确的映射,我完全可以接受史诗般的失败。我对 Rust 的理解很简单,我需要说服它 T 确实实现了 U 并且可以隐式或显式地转换为它。 cast,我的意思是一个具体但通用的类型可以转换为一个 trait 对象,因为它实现了它(我意识到大多数人要求另一个方向,我理解那里的复杂性)。
包括我所拥有的一切会有点多;但是,这里有一些上下文。考虑一下我们可以将工厂函数归结为(这可行):
Rc<dyn Fn(&ServiceProvider) -> Rc<dyn Any>>
目前,我有一个类似于(删节)的构建器:
pub struct ServiceDescriptorBuilder<TSvc: Any + ?Sized, TImpl> {
_phantom_svc: PhantomData<TSvc>,
_phantom_impl: PhantomData<TImpl>,
}
impl<TSvc: Any + ?Sized, TImpl> ServiceDescriptorBuilder<TSvc, TImpl> {
pub fn from<F>(self, factory: F) -> ServiceDescriptor
where
F: Fn(&ServiceProvider) -> Rc<TSvc> + 'static, {
// convert to factory
let factory = Rc::new(move |sp| Rc::new(factory(sp)));
}
}
这完全适用于:
let descriptor = ServiceDescriptorBuilder::<dyn Foo, FooImpl>::new(/* unimportant */)
.from(|sp| Rc::new(FooImpl::default()))
我的目标是在工厂关闭中删除额外的Rc 声明。 所有实例都包含在Rc 中。瞬态生命周期总是得到一个新的Rc,计数为 1,而单例有一个克隆的Rc。我想要做的是(例如减去Rc):
let descriptor = ServiceDescriptorBuilder::<dyn Foo, FooImpl>::new(/* unimportant */)
.from(|sp| FooImpl::default())
我可以得到第二个更简单的变体来编译,但它在运行时会失败——原因很明显。我需要说服 Rust 使用拥有值为FooImpl 的Rc<dyn Foo>。这对于泛型来说似乎是不可能的。关联类型也许是可能的,但这只是一种选择,如果它仍然产生简洁的语法。真正让我无法理解的是编译器本身可以做到的magic。理解Rc<FooImpl>是协变的,可以满足Rc<dyn Foo>,似乎没有问题。
我有一些工作,所以这并不是真正的阻塞,但让我很恼火的是,这不能以更简单的形式实现。也许这只是语言的当前限制。其他一些语言将允许将其表示为where TImpl: TSvc。我不会假装理解为什么这是困难或尚不支持,但其他语言已经弄清楚了。我知道已经有一个限制,您不能将类型限制为特征。也许这是使这更简洁所需的唯一功能。
我满足于等待,但似乎有一些不安全的方法可以让Rc 拥有FooImpl 的实例,但表示为Rc<dyn Foo>。除非我有严重的误解,否则这就是编译器最终产生的结果。我不清楚如何强制或复制它。
我不喜欢使用宏来解决这个问题,但我认为这是一个可以接受的解决方案;特别是,如果它可以作为实现细节隐藏起来(来自调用者/开发者)。虽然我当前的解决方案确实有效并且更加冗长,但它确实提供了某种程度的编译器检查,因为您不能只返回 anything。删除此检查的更简洁的语法(使用transmute 或其他东西)可能不值得权衡。
建议?
【问题讨论】:
标签: rust