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

huangapple go评论63阅读模式
英文:

How to properly use zero-sized types for constraining the public api?

问题

我正在阅读《Rust for Rustaceans》,它讨论了使用零大小类型来建模库的API。它提供了一个应用程序的框架,但没有展示实际的实现,当我尝试实现它时,失败了。

我需要帮助实现默认值,因为它目前抱怨在创建对象时没有为阶段提供值。实际上,我以为由于它是PhantomData,我不需要提供任何值。

我还需要帮助实现launch。我希望能够返回self以重用现有对象,但它抱怨它是不同的类型。将 `self as Rocket<Launched>` 强制转换也不起作用。

如何使这个框架正常工作的正确方法是什么?

```rust
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!("加速"); }
    pub fn decelerate(&mut self) { println!("减速"); }
}

impl<Stage> Rocket<Stage> {
    pub fn color(&self) { println!("蓝色"); }
    pub fn weight(&self) { println!("10公斤"); }
}

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

I'm reading Rust for Rustaceans and it talks about the use of zero-sized types for modeling the api of a library. It gives the skeleton of an application but doesn't show the actual implementation and when I tried implementing it, it failed.

I need help implementing default because it is currently complaining that I'm not providing a value for stage when creating the object. I actually thought that because it is PhantomData I wouldn't have to provide any value.

I also need help implementing launch. I wanted to be able to just return self to reuse the existing object but it complains it's a different type. Casting self as Rocket&lt;Launched&gt; doesn't work either.

What is the proper way to get this skeleton to work?

struct Grounded;
struct Launched;

struct Rocket&lt;Stage = Grounded&gt; {
    stage: std::marker::PhantomData&lt;Stage&gt;,
}

impl Default for Rocket&lt;Grounded&gt; {
    fn default() -&gt; Self { }
}

impl Rocket&lt;Grounded&gt; {
    pub fn launch(self) -&gt; Rocket&lt;Launched&gt; { }
}

impl Rocket&lt;Launched&gt; {
    pub fn accelerate(&amp;mut self) { println!(&quot;Accelerating&quot;); }
    pub fn decelerate(&amp;mut self) { println!(&quot;Decelerating&quot;); }
}

impl&lt;Stage&gt; Rocket&lt;Stage&gt; {
    pub fn color(&amp;self) { println!(&quot;blue&quot;); }
    pub fn weight(&amp;self) { println!(&quot;10kg&quot;); }
}

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

答案1

得分: 2

每个字段都必须被初始化,即使是 ZST 字段,比如 `PhantomData`。
你可以这样做:
```rust
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,而且在没有它的情况下也能很好地工作。


<details>
<summary>英文:</summary>

Every field has to be initialized that&#39;s also true for ZST fields such as `PhantomData`
You can do so like this:
```rust
use std::marker::PhantomData;
impl Default for Rocket&lt;Grounded&gt; {
    fn default() -&gt; Self { 
        Self {
            stage: PhantomData,
        }
    }
}

You can remove some of that boilerplate by just deriving Default though:

#[derive(Default)]
struct Grounded;
#[derive(Default)]
struct Rocket&lt;Stage = Grounded&gt; {
    stage: PhantomData&lt;Stage&gt;,
}

Since Rocket&lt;Grounded&gt; and Rocket&lt;Launched&gt; are two different types you can't use one to initialize the other. You have to create a new rocket:

impl Rocket&lt;Grounded&gt; {
    pub fn launch(self) -&gt; Rocket&lt;Launched&gt; {
        Rocket {
            stage: PhantomData,
        }
    }
}

Though it's not exactly clear why you need PhantomData at all since the stages are already ZSTs and it all works just fine without it

huangapple
  • 本文由 发表于 2023年2月24日 07:47:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/75551408.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定