英文:
Conflicting trait implementation with type Identity<A> = A
问题
我对Rust相对不熟悉,作为一个让我的手更加熟悉它的项目,我决定实现一个关于范畴论和函数式编程的库。长话短说,有一个叫做Monad
的trait:
pub trait Monad {
// 关联类型和函数在这里定义
}
在Rust中,Option
是Monad,所以我像这样写:
impl<X> Monad for Option<X> {
// 在这里实现
}
到目前为止一切都正常。我决定添加一个特殊的Monad,叫做Identity
Monad,这是一个著名且方便的Monad:
type Identity<A> = A;
impl<X> Monad for Identity<X> {
// 在这里实现
}
但是,就在这里,我收到了这个错误:conflicting implementations for Option<_>
。现在我已经阅读了这个Stack Overflow帖子,对于那个用例来说这是有道理的,但是对于我的情况,就我所能思考的,因为Identity
是一个类型(而不是trait),它可能永远不会是“Option
”。有人可以告诉我为什么会发生这种情况以及我可以怎么做吗?
英文:
I'm fairly new to Rust, and as a project to make my hands more used to it, I decided to implement a library about category theory and functional programming in general. Long story short, there is a trait called Monad
:
pub trait Monad {
// Associated types and functions here
}
And in rust, Option
s are Monads, so I went like this:
impl<X> Monad for Option<X> {
// Implementation here
}
Everything's working properly so far. I decided to add a special Monad called Identity
Monad, which is a famous and handy one:
type Identity<A> = A;
impl<X> Monad for Identity<X> {
// Implementation here
}
And right there, I get this error: conflicting implementations for Option<_>
. Now I have read this SO post, and it makes sense for that use case, but about mine, as far as I can think through, since Identity
is a type (not a trait) it may never be the "Option
". Can anyone let me know about why's that happening and what I can do for it?
答案1
得分: 4
They conflict because Identity<Option<X>>
is the same type as Option<X>
.
type
does not define a new type, it defines an alias for an existing type; Identity<X>
is just another name for X
.
You might be interested in the newtype pattern where you create a single-struct field to define a new semantic type wrapping another:
struct Identity<X>(X);
Now Identity<X>
is a different type from X
, and the implementations would no longer conflict.
If you decorate Identity
with #[repr(transparent)]
then it's even possible to safely transmute &X
to &Identity<X>
, which might be useful depending on the members of Monad
.
英文:
They conflict because Identity<Option<X>>
is the same type as Option<X>
.
type
does not define a new type, it defines an alias for an existing type; Identity<X>
is just another name for X
.
You might be interested in the newtype pattern where you create a single-struct field to define a new semantic type wrapping another:
struct Identity<X>(X);
Now Identity<X>
is a different type from X
, and the implementations would no longer conflict.
If you decorate Identity
with #[repr(transparent)]
then it's even possible to safely transmute &X
to &Identity<X>
, which might be useful depending on the members of Monad
.
答案2
得分: 1
如已提及的,问题在于 type
定义了一个别名,一个现有类型的新名称,所以 Identity<T> = T
,因此你的 impl<X> Monad for Identity<X>
等同于 impl<X> Monad for X
。
我想提供一些额外的上下文,可能会有用,假设你(或其他有同样问题的读者)是一个 Haskell 程序员。
在 Haskell 中,有三种类型声明:data
、type
和 newtype
。以下是它们与 Rust 对应的方式:
-
Haskell 的
data
直接对应于 Rust 的enum
— 它声明了一个新的独立类型,用于保存数据并可以有多个构造器(Haskell)/变体(Rust)。Rust 还提供了struct
,它没有变体,而是可以具有私有字段并使用点符号进行访问(有点类似于 Haskell 的记录语法)。 -
Haskell 的
type
直接对应于 Rust 的type
— 它们为现有类型定义别名。 -
Haskell 的
newtype
在 Rust 中没有直接的对应。newtype
定义了一个新的独立类型,该类型只有一个数据字段,预期此类型在运行时与该字段完全相同(没有额外的间接引用,也没有额外的惰性计算)。但是,在 Rust 中,默认假设 一个
struct
没有额外的成本,也没有不同于仅使用其字段或字段的行为。(而且没有惰性计算,因此没有惰性区别。)因此,当 Rust 程序员谈论“newtype 模式”时,他们指的是 模式 — 而不是语言特性 — 即声明一个带有一个字段的struct
来引入一个独立的类型。你可以进一步要求使用
#[repr(transparent)]
来保证 newtype 被表示为与旧类型完全相同,只有当你打算使用unsafe
代码或一个库(例如bytemuck
)来访问一个类型的内存,就需要这样做。#[repr(transparent)] struct Identity<X>(X);
这是 Rust 中“newtype 模式”的最大形式。
英文:
As has already been mentioned, the problem is that type
defines an alias, a new name, for an existing type, so Identity<T> = T
and therefore your impl<X> Monad for Identity<X>
is equivalent to just impl<X> Monad for X
.
I would like to provide some additional context that may be useful, presuming that you (or some other reader with the same question) are a Haskell programmer.
In Haskell, there are 3 kinds of type declarations: data
, type
, and newtype
. Here is how they correspond to Rust:
-
Haskell's
data
directly corresponds to Rust'senum
— it declares a new distinct type that holds data and can have multiple constructors (Haskell) / variants (Rust). Rust also offers thestruct
which does not have variants, and in exchange can have private fields and access with dot notation (somewhat like Haskell record syntax). -
Haskell's
type
directly corresponds to Rust'stype
— they define aliases for existing types. -
Haskell's
newtype
has no direct analogue in Rust.newtype
defines a new distinct type which has exactly one data field, and it is expected that this type is represented at run time exactly like that field (with no additional indirection, and no additional laziness).However, in Rust, the default assumption is that a
struct
has no additional cost and no different behavior than just using its field or fields. (And there is no laziness, so no laziness distinction.) So, when Rust programmers speak of “the newtype pattern” they mean the pattern — not the language feature — of declaring astruct
with one field to introduce a distinct type.You can further ask for a guarantee that the newtype is represented exactly the same as the old type, by using
#[repr(transparent)]
. This is only necessary if you intend to access the memory of one type as if it were the other, usingunsafe
code or a library (such asbytemuck
) that does it for you.#[repr(transparent)] struct Identity<X>(X);
This is the maximal form of the “newtype pattern” in Rust.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论