rust 为什么有些代码的类型参数只在trait边界中使用,而没有PhantomData编译?

ijxebb2r  于 2023-08-05  发布在  其他
关注(0)|答案(1)|浏览(67)

这段代码无法编译(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)
    }
}

字符串
编译器说

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,代码将无法编译:

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));
}


为什么?为什么?

v7pvogib

v7pvogib1#

我认为错误是不正确的。这对于具有泛型和关联类型的常规特征更有意义。
这会编译。

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上)。因此,关联的类型算作使用,而泛型则不算。
请注意,这对大多数代码来说不是问题,因为您使用的是shouldn't put trait bounds on structs except in specific cases。您只需对任何泛型字段使用未绑定泛型,然后将它们绑定到实现中的特征。这样就可以编译了。

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,特别是当您需要实现某个特性时,但是在大多数情况下,您可以重新排列泛型以避免它。

相关问题