rust 如何正确使用零大小类型来约束公共API?

7vux5j2d  于 2023-03-02  发布在  其他
关注(0)|答案(1)|浏览(108)

我正在阅读Rust for Rustaceans,它讨论了零大小类型在库的API建模中的使用,它给出了应用程序的框架,但没有显示实际的实现,当我尝试实现它时,它失败了。
我需要帮助实现默认值,因为它现在抱怨我在创建对象时没有为stage提供值。我实际上认为,因为它是PhantomData,所以我不必提供任何值。
我还需要帮助实现launch。我希望能够只返回self来重用现有对象,但它抱怨它是一个不同的类型。强制转换self as Rocket<Launched>也不起作用。
让这个 backbone 工作的正确方法是什么?

struct Grounded;
struct Launched;

struct Rocket<Stage = Grounded> {
    stage: std::marker::PhantomData<Stage>,
}

impl Default for Rocket<Grounded> {
    fn default() -> Self { }
}

impl Rocket<Grounded> {
    pub fn launch(self) -> Rocket<Launched> { }
}

impl Rocket<Launched> {
    pub fn accelerate(&mut self) { println!("Accelerating"); }
    pub fn decelerate(&mut self) { println!("Decelerating"); }
}

impl<Stage> Rocket<Stage> {
    pub fn color(&self) { println!("blue"); }
    pub fn weight(&self) { println!("10kg"); }
}

fn main() {
    let rocket: Rocket = Default::default();
}
aiazj4mn

aiazj4mn1#

每个字段都必须初始化,这对于ZST字段(如PhantomData)也是如此。您可以这样做:

use std::marker::PhantomData;
impl Default for Rocket<Grounded> {
    fn default() -> Self { 
        Self {
            stage: PhantomData,
        }
    }
}

您可以通过直接导出Default来删除其中的一些样板文件:

#[derive(Default)]
struct Grounded;
#[derive(Default)]
struct Rocket<Stage = Grounded> {
    stage: PhantomData<Stage>,
}

因为Rocket<Grounded>Rocket<Launched>是两种不同的类型,你不能用一个初始化另一个。你必须创建一个新的火箭:

impl Rocket<Grounded> {
    pub fn launch(self) -> Rocket<Launched> {
        Rocket {
            stage: PhantomData,
        }
    }
}

尽管还不清楚为什么需要PhantomData,因为这些级已经是ZST,而且都是works just fine without it

相关问题