英文:
Why does some code with type parameters used only in trait bounds compile without PhantomData?
问题
这段代码无法编译,编译器报告以下错误:
error[E0392]: parameter `T` is never used
--> src/main.rs:3:15
|
3 | struct Mapper<T, U, F>
| ^ unused parameter
|
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
= help: if you intended `T` to be a const parameter, use `const T: usize` instead
error[E0392]: parameter `U` is never used
--> src/main.rs:3:18
|
3 | struct Mapper<T, U, F>
| ^ unused parameter
|
= help: consider removing `U`, referring to it in a field, or using a marker such as `PhantomData`
= help: if you intended `U` to be a const parameter, use `const U: usize` instead
然而,当你只为 T
添加 PhantomData
时,代码可以编译:
use std::marker::PhantomData;
struct Mapper<T, U, F>
where
F: Fn(T) -> U,
{
mapper: F,
_t: PhantomData<T>,
// _u: PhantomData<U>,
}
impl<T, U, F> Mapper<T, U, F>
where
F: Fn(T) -> U,
{
fn new(mapper: F) -> Self {
Self {
mapper,
_t: PhantomData,
// _u: PhantomData,
}
}
fn call(&self, t: T) -> U {
(self.mapper)(t)
}
}
然而,如果你只为 U
添加 PhantomData
,代码将无法编译。这是因为编译器会检查结构体字段是否被使用,如果没有字段使用参数 U
,编译器将报告错误。在这种情况下,没有任何字段使用 U
,所以编译器认为 U
是未使用的。
如果你打算保留 U
参数但不在结构体中使用它,你可以使用注释或其他方式来引用它,以避免编译错误。或者你可以考虑使用 PhantomData
为 U
创建一个字段,就像你在第一个示例中为 T
所做的那样。这将告诉编译器 U
参数是有意义的,并且不会被删除。
英文:
This code doesn't compile (playground):
struct Mapper<T, U, F>
where
F: Fn(T) -> U,
{
mapper: F,
// _t: PhantomData<T>,
// _u: PhantomData<U>,
}
impl<T, U, F> Mapper<T, U, F>
where
F: Fn(T) -> U,
{
fn new(mapper: F) -> Self {
Self {
mapper,
// _t: PhantomData,
// _u: PhantomData,
}
}
fn call(&self, t: T) -> U {
(self.mapper)(t)
}
}
The compiler says
error[E0392]: parameter `T` is never used
--> src/main.rs:3:15
|
3 | struct Mapper<T, U, F>
| ^ unused parameter
|
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
= help: if you intended `T` to be a const parameter, use `const T: usize` instead
error[E0392]: parameter `U` is never used
--> src/main.rs:3:18
|
3 | struct Mapper<T, U, F>
| ^ unused parameter
|
= help: consider removing `U`, referring to it in a field, or using a marker such as `PhantomData`
= help: if you intended `U` to be a const parameter, use `const U: usize` instead
However, this code compiles though I add PhantomData
only for T
:
use std::marker::PhantomData;
struct Mapper<T, U, F>
where
F: Fn(T) -> U,
{
mapper: F,
_t: PhantomData<T>,
// _u: PhantomData<U>,
}
impl<T, U, F> Mapper<T, U, F>
where
F: Fn(T) -> U,
{
fn new(mapper: F) -> Self {
Self {
mapper,
_t: PhantomData,
// _u: PhantomData,
}
}
fn call(&self, t: T) -> U {
(self.mapper)(t)
}
}
On the other hand, if I add PhantomData
only for U
, the code doesn't compile:
use std::marker::PhantomData;
struct Mapper<T, U, F>
where
F: Fn(T) -> U,
{
mapper: F,
// _t: PhantomData<T>,
_u: PhantomData<U>,
}
impl<T, U, F> Mapper<T, U, F>
where
F: Fn(T) -> U,
{
fn new(mapper: F) -> Self {
Self {
mapper,
// _t: PhantomData,
_u: PhantomData,
}
}
fn call(&self, t: T) -> U {
(self.mapper)(t)
}
}
fn main() {
let mapper = Mapper::new(|a: usize| -> isize { -(a as isize) });
println!("{}", mapper.call(3));
}
Why?
答案1
得分: 3
我认为错误是不正确的。将泛型和关联类型与常规特性更相符。
这个编译。
use std::ops::Add;
pub struct A<X, Z>
where
X: Add<Output = Z>,
{
pub adder: X,
}
这个不行。
pub struct A<X, Y>
where
X: Add<Y>,
{
pub adder: X,
}
这会出现两个错误,尽管 Y
是唯一的实际错误。
pub struct A<X, Y, Z>
where
X: Add<Y, Output = Z>,
{
pub adder: X,
}
如果你查看 Fn
的定义,你会看到参数是一个泛型,而返回值是一个关联类型(在 FnOnce
上)。所以关联类型算作使用,而泛型则不算。
请注意,对于大多数代码来说,这不是问题,因为你 不应该在结构体上放置特性约束,除非在特定情况下。你只需对任何泛型字段使用无界泛型,然后在实现中将它们绑定到特性。这可以很好地编译。
pub struct A<X> {
pub adder: X,
}
impl<X> A<X>
{
pub fn new<Y>(x: X, y: Y) -> Self
where
X: Add<Y, Output = X>,
{
Self {
adder: x + y,
}
}
}
可能仍然需要 PhantomData
,特别是如果你需要实现某个特性,但在大多数情况下,你可以重新排列你的泛型以避免它。
英文:
I believe the error is incorrect. This makes more sense with a regular trait with generics and associated types.
This compiles.
use std::ops::Add;
pub struct A<X, Z>
where
X: Add<Output = Z>,
{
pub adder: X,
}
This does not.
pub struct A<X, Y>
where
X: Add<Y>,
{
pub adder: X,
}
This gives two errors, even though Y
is the only actual error.
pub struct A<X, Y, Z>
where
X: Add<Y, Output = Z>,
{
pub adder: X,
}
And if you look at the definition of Fn
, you'll see that the parameter is a generic, while the return is an associated type (on FnOnce
). So associated types count as usage, while generics do not.
Note that this is not an issue for most code, since you shouldn't put trait bounds on structs except in specific cases. You would simply use unbound generics for any generic fields, and then bind them to traits in the implementation. This compiles fine.
pub struct A<X> {
pub adder: X,
}
impl<X> A<X>
{
pub fn new<Y>(x: X, y: Y) -> Self
where
X: Add<Y, Output = X>,
{
Self {
adder: x + y,
}
}
}
There may still be need for PhantomData
, especially if you need to implement a certain trait, but in most cases you can rearrange your generics to avoid it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论