使用类似泛型的构造的子类型

时间:2018-11-10 19:26:52

标签: generics types rust

我有一个结构,确切地说,存在两个版本。也就是说,该结构具有一个属性,该属性描述了它是哪个版本(具有两个变体的boolenum)作为构造函数中的参数传递。结构在编译时就知道哪个版本。在该结构的大多数方法中,根据属性的值,调用了相应的方法(在此结构的属性上)。这样会在整个结构的impl中产生许多if语句。

我考虑过将所有代码都移到一个特征中,但这似乎不合适:动态调度不是必需的,几乎所有方法都没有self参数,所有属性的setters / getters将是需要。我仍然剩下两个相同的结构声明。而且,该特征没有描述其他结构应实现的任何一般行为。

如果我可以创建此结构的通用版本并实例化这两个版本之一,那将是理想的选择。由于缺少更好的词,我只能使用一种具有不同行为的方法来构造我的结构的两个“子类型”。这样的事情有可能吗?

它关系到性能至关重要的应用程序,并且此结构上的方法将被多次调用。不是为了维护性,我只是复制所有代码。我将创建两个几乎相同的结构,其中在方法内部,有一个版本的外部方法被调用,或者另一个版本。

1 个答案:

答案 0 :(得分:2)

a trait用于具有多种实现的行为。您可以使用多种方式进行组合,这是一种:

use std::marker::PhantomData;

trait Core {
    fn print();
}

#[derive(Debug, Default)]
struct PrintA;
impl Core for PrintA {
    fn print() {
        print!("a")
    }
}

#[derive(Debug, Default)]
struct PrintB;
impl Core for PrintB {
    fn print() {
        print!("b")
    }
}

#[derive(Debug, Default)]
struct Thing<C>(PhantomData<C>);

impl<C: Core> Thing<C> {
    fn common() {
        print!(">");
        C::print();
        println!("<")
    }
}

fn main() {
    Thing::<PrintA>::common();
    Thing::<PrintB>::common();
}

或另一个:

trait Core {
    fn select<'a>(left: &'a i32, right: &'a i32) -> &'a i32;
}

#[derive(Debug, Default)]
struct Left;
impl Core for Left {
    fn select<'a>(left: &'a i32, _right: &'a i32) -> &'a i32 {
        left
    }
}

#[derive(Debug, Default)]
struct Right;
impl Core for Right {
    fn select<'a>(_left: &'a i32, right: &'a i32) -> &'a i32 {
        right
    }
}

#[derive(Debug, Default)]
struct Thing<C> {
    kind: C,
    left: i32,
    right: i32,
}

impl Thing<Left> {
    fn new_left(left: i32, right: i32) -> Self {
        Self {
            left,
            right,
            kind: Left,
        }
    }
}
impl Thing<Right> {
    fn new_right(left: i32, right: i32) -> Self {
        Self {
            left,
            right,
            kind: Right,
        }
    }
}

impl<C: Core> Thing<C> {
    fn add_one(&self) -> i32 {
        self.select() + 1
    }

    fn select(&self) -> &i32 {
        C::select(&self.left, &self.right)
    }
}

pub fn l() -> i32 {
    let l = Thing::new_left(100, 200);
    l.add_one()
}

pub fn r() -> i32 {
    let r = Thing::new_right(100, 200);
    r.add_one()
}

值得注意的是,最后一个示例编译为以下LLVM IR:

define i32 @_playground_l() {
start:
  ret i32 101
}

define i32 @_playground_r()  {
start:
  ret i32 201
}
  

我考虑过将所有代码都转换为特征,但这似乎不合适:动态分配不是必需的,几乎所有方法都没有self参数

    特性不表示动态分配。请参见单态化
  • 特征方法不需要self
  

这不是出于可维护性,我只复制所有代码

如果无法处理特质,听起来像macros合适的地方。