Rust 构建器模式是否必须使用冗余结构代码?

Do Rust builder patterns have to use redundant struct code?

我在查看 Method syntax section of the Rust documentation 时遇到了构建器模式的示例。下面示例中的 CircleBuilder 结构与 Circle 结构完全相同。这段冗余代码似乎违反了通常的编程规范。

我明白为什么示例创建了一个新结构,因为创建者不想针对原始 Circle 结构实现构建器方法。这很好,但是有没有办法重写这个例子,这样就没有冗余——但仍然保持 main() 函数中漂亮的构建器界面完好无损?

我试图创建一个空结构或只有一个一次性元素的结构,但这没有用。

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

struct CircleBuilder {
    x: f64,
    y: f64,
    radius: f64,
}

impl CircleBuilder {
    fn new() -> CircleBuilder {
        CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, }
    }

    fn x(&mut self, coordinate: f64) -> &mut CircleBuilder {
        self.x = coordinate;
        self
    }

    fn y(&mut self, coordinate: f64) -> &mut CircleBuilder {
        self.y = coordinate;
        self
    }

    fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
        self.radius = radius;
        self
    }

    fn finalize(&self) -> Circle {
        Circle { x: self.x, y: self.y, radius: self.radius }
    }
}

fn main() {
    let c = CircleBuilder::new()
                .x(1.0)
                .y(2.0)
                .radius(2.0)
                .finalize();

    println!("area: {}", c.area());
    println!("x: {}", c.x);
    println!("y: {}", c.y);
}

这似乎是宏可以做的事情。快速搜索发现 derive_builder and builder_macro 个似乎实现了此功能的 crate。

Do Rust builder patterns have to use redundant struct code?

没有。但有时他们 可能 。例如,考虑一下我们是否希望在我们的构造函数周围有特殊的逻辑(或者甚至只是复杂的逻辑):

/// Width must always be greater than height!
struct HorizontalEllipse {
    width: f64,
    height: f64,
}

impl HorizontalEllipse {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.width / 2.0) * (self.height / 2.0)
    }
}

struct HorizontalEllipseBuilder {
    width: f64,
    height: f64,
}

impl HorizontalEllipseBuilder {
    fn new() -> HorizontalEllipseBuilder {
        HorizontalEllipseBuilder {
            width: 0.0,
            height: 0.0,
        }
    }

    fn width(&mut self, width: f64) -> &mut HorizontalEllipseBuilder {
        self.width = width;
        self
    }

    fn height(&mut self, height: f64) -> &mut HorizontalEllipseBuilder {
        self.height = height;
        self
    }

    fn finalize(&self) -> Result<HorizontalEllipse, String> {
        let HorizontalEllipseBuilder { height, width } = *self;
        if height >= width {
            Err("This is not horizontal".into())
        } else {
            Ok(HorizontalEllipse { width, height })
        }
    }
}

fn main() {
    let c = HorizontalEllipseBuilder::new()
        .width(1.0)
        .height(2.0)
        .finalize()
        .expect("not a valid ellipse");

    println!("area: {}", c.area());
    println!("width: {}", c.width);
    println!("height: {}", c.height);
}

现在 HorizontalEllipse 知道 width > height 总是正确的。我们已将该检查从许多可能的地方(每个方法)移到一个地方,即构造函数。然后我们将构造函数移动到一个新类型,因为它很复杂(不是真的,但真正复杂的例子通常......很复杂)。

我见过的许多建筑商也有 "enhanced" 种实物:

#[derive(Debug)]
struct Person {
    name: String,
}

#[derive(Debug, Default)]
struct PersonBuilder {
    name: Option<String>,
}

impl PersonBuilder {
    fn name(self, name: &str) -> Self {
        PersonBuilder { name: Some(name.into()), ..self }
    }

    fn build(self) -> Person {
        Person {
            name: self.name.unwrap_or_else(|| "Stefani Joanne Angelina Germanotta".into()),
        }
    }
}

fn main() {
    let person = PersonBuilder::default().build();
    println!("{:?}", person);

    let person = PersonBuilder::default().name("krishnab").build();
    println!("{:?}", person);
}

您在本书的示例中看不到这一点,因为它试图变得更简单并且不涉及所有权问题。