英文:
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<Launched>
doesn't work either.
What is the proper way to get this skeleton to work?
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();
}
答案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's also true for ZST fields such as `PhantomData`
You can do so like this:
```rust
use std::marker::PhantomData;
impl Default for Rocket<Grounded> {
fn default() -> Self {
Self {
stage: PhantomData,
}
}
}
You can remove some of that boilerplate by just deriving Default
though:
#[derive(Default)]
struct Grounded;
#[derive(Default)]
struct Rocket<Stage = Grounded> {
stage: PhantomData<Stage>,
}
Since Rocket<Grounded>
and Rocket<Launched>
are two different types you can't use one to initialize the other. You have to create a new rocket:
impl Rocket<Grounded> {
pub fn launch(self) -> Rocket<Launched> {
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论