有没有更好的方法来使用From和Into来共享一个共同的内部字段

时间:2016-05-31 20:14:17

标签: rust

我有许多包裹Builder结构的结构。这些包装器提供了对此Builder的专门行为。在这些包装器之间进行转换应该很容易,因为它们基本上只是具有专门方法的Builder。我已经编写了一些代码来说明 - 对于一些简单的东西来说,似乎有很多样板。还有更好的方法吗?

struct Builder;

trait ObjectBuilder: Into<Builder> + From<Builder> {
  fn from_other<T: ObjectBuilder>(x: T) -> Self {
     let builder = x.into();
     builder.into()
  }
}

struct OtherBuilder {
   inner: Builder
}

impl From<Builder> for OtherBuilder {
    fn from(x: Builder) -> OtherBuilder {
        OtherBuilder {
            inner: x
        }
    }
}

impl Into<Builder> for OtherBuilder {
    fn into(self) -> Builder {
        self.inner
    }
}

struct OtherOtherBuilder {
   inner: Builder
}

impl From<Builder> for OtherOtherBuilder {
    fn from(x: Builder) -> OtherOtherBuilder {
        OtherOtherBuilder {
            inner: x
        }
    }
}

impl Into<Builder> for OtherOtherBuilder {
    fn into(self) -> Builder {
        self.inner
    }
}

impl ObjectBuilder for OtherBuilder {}
impl ObjectBuilder for OtherOtherBuilder {}

fn main() {
    let x = Builder;
    let y: OtherBuilder = x.into();
    let z: OtherOtherBuilder = ObjectBuilder::from_other(y);
    let y = OtherBuilder::from_other(z);
}

Playground URL

Gist URL

2 个答案:

答案 0 :(得分:2)

您可以定义Builder的扩展特征,而不是定义包装器结构,即仅在Builder上实现的特性,它提供可以使用方法调用语法调用的其他函数({{1而不是builder.func())。然后,您只能导入与适当范围相关的特征(无论是模块范围,功能范围等)。

这种方法的一个缺点是,如果你有多个特性提供具有相同名称的方法,那么使用方法调用语法调用这些方法将是不明确的(如果在相关范围中导入了多个线程),作为编译器我不知道你指的是哪种特质。当然,您可以使用普通函数语法(func(builder))消除调用的歧义,但这并不漂亮。在这种情况下,也许包装器是一种更好的方法。

答案 1 :(得分:2)

您可以使用宏来减少代码中的重复:

struct Builder;

trait ObjectBuilder: Into<Builder> + From<Builder> {
  fn from_other<T: ObjectBuilder>(x: T) -> Self {
     let builder = x.into();
     builder.into()
  }
}

macro_rules! builder {
    ($name:ident) => {
        struct $name {
           inner: Builder
        }

        impl From<Builder> for $name {
            fn from(x: Builder) -> $name {
                $name {
                    inner: x
                }
            }
        }

        impl Into<Builder> for $name {
            fn into(self) -> Builder {
                self.inner
            }
        }

        impl ObjectBuilder for $name {}
    }
}

builder!(OtherBuilder);
builder!(OtherOtherBuilder);

fn main() {
    let x = Builder;
    let y: OtherBuilder = x.into();
    let z: OtherOtherBuilder = ObjectBuilder::from_other(y);
    let y = OtherBuilder::from_other(z);
}