rust 不带类型参数的泛型类型上的泛型结构

dgjrabp2  于 2023-08-05  发布在  其他
关注(0)|答案(2)|浏览(103)

在Rust中可以做这样的事情吗?

trait Foo<T> {}

struct A;
struct B;

struct Bar<T: Foo> {
    a: T<A>,
    b: T<B>
}

字符串
我知道我可以只对Bar使用两个参数,但我认为必须有更好的方法来实现这一点。
我想实现一个Graph结构。因为我不能只将节点和边绑定到它们的父生命周期,所以我希望有像Rc这样的东西。但是,有时可能需要Graph,可以从多个线程访问。因此,我必须同时使用RcArc实现。
这就是Foo的好处:我为RcArc实现了FooFoo需要Deref),并使用了一个绑定到Foo的参数T。这就是我想要的单线程和多线程使用一个结构。

xdnvmnnf

xdnvmnnf1#

您可以使用 * 泛型关联类型 *(GAT)和族模式:

trait Family {
    type Container<T>;
}

struct A;
struct B;

struct Bar<T: Family> {
    a: T::Container<A>,
    b: T::Container<B>,
}

字符串
然后可以定义两个族:

struct BoxFamily;
impl Family for BoxFamily {
    type Container<T> = Box<T>;
}

struct VecFamily;
impl Family for VecFamily {
    type Container<T> = Vec<T>;
}


并使用它:

let boxes: Bar<BoxFamily> = Bar {
    a: Box::new(A),
    b: Box::new(B),
};

let vecs: Bar<VecFamily> = Bar {
    a: vec![A, A],
    b: vec![B],
};


Playground
正如你所看到的,它比人们所希望的要稍微复杂一些:你不能只说Bar<Vec>,例如,但必须通过额外的家庭类型。但它有效!
有关包含有关该主题的更多一般信息的较旧答案(在GAT存在之前),请单击here

bbmckpt7

bbmckpt72#

在某种程度上,Rust * 确实 * 有看起来很像HKT的东西(请参阅Lukas的回答,以了解它们是什么),尽管有一些可以说是笨拙的语法。
首先,您需要为所需的指针类型定义接口,这可以使用泛型trait来完成。举例来说:

trait SharedPointer<T>: Clone {
    fn new(v: T) -> Self;
    // more, eg: fn get(&self) -> &T;
}

字符串
加上一个泛型trait,它定义了一个关联的类型,这是你真正想要的类型,它必须实现你的接口:

trait Param<T> {
    type Pointer: SharedPointer<T>;
}


接下来,我们为我们感兴趣的类型实现该接口:

impl<T> SharedPointer<T> for Rc<T> {
    fn new(v: T) -> Self {
        Rc::new(v)
    }
}
impl<T> SharedPointer<T> for Arc<T> {
    fn new(v: T) -> Self {
        Arc::new(v)
    }
}


并定义一些实现上述Param trait的虚拟类型。这是关键部分;我们可以有一个类型(RcParam),它为任何T实现了Param<T>,包括能够提供一个类型,这意味着我们模拟了一个更高级的类型。

struct RcParam;
struct ArcParam;

impl<T> Param<T> for RcParam {
    type Pointer = Rc<T>;
}

impl<T> Param<T> for ArcParam {
    type Pointer = Arc<T>;
}


最后,我们可以使用它:

struct A;
struct B;

struct Foo<P: Param<A> + Param<B>> {
    a: <P as Param<A>>::Pointer,
    b: <P as Param<B>>::Pointer,
}

impl<P: Param<A> + Param<B>> Foo<P> {
    fn new(a: A, b: B) -> Foo<P> {
        Foo {
            a: <P as Param<A>>::Pointer::new(a),
            b: <P as Param<B>>::Pointer::new(b),
        }
    }
}

fn main() {
    // Look ma, we're using a generic smart pointer type!
    let foo = Foo::<RcParam>::new(A, B);
    let afoo = Foo::<ArcParam>::new(A, B);
}


Playground

相关问题