【问题标题】:How to define a function-local type alias of the function's type parameters (or their associated types)?如何定义函数类型参数(或其关联类型)的函数局部类型别名?
【发布时间】:2019-02-06 16:52:43
【问题描述】:

我有一个通用函数 foo 具有一些复杂的特征界限:

use std::ops::Index;

// This trait is just as an example
trait Float {
    const PI: Self;
    fn from_f32(v: f32) -> Self;
}
// impl Float for f32, f64 ...

fn foo<C>(container: &C)
where
    C: Index<u32>,
    <C as Index<u32>>::Output: Float,
{
    // ...
}

我现在需要在函数中使用 &lt;C as Index&lt;u32&gt;&gt;::Output 类型(例如,通过 ::PI 或说 ::from_f32(3.0) 获取 π)。但是这种类型很长,手工输入很长,并且使整个代码非常冗长且难以阅读。 (注意:在我的真实代码中,实际的类型更长更丑。)

为了解决这个问题,我尝试创建一个函数本地类型别名:

// Inside of `foo`:
type Floaty = <C as Index<u32>>::Output;

但这会导致这个错误:

error[E0401]: can't use type parameters from outer function
  --> src/lib.rs:16:20
   |
10 | fn foo<C>(container: &C)
   |    --- - type variable from outer function
   |    |
   |    try adding a local type parameter in this method instead
...
16 |     type Floaty = <C as Index<u32>>::Output;
   |                    ^ use of type variable from outer function

因此,就像其他项目一样,type 别名也会被处理,无论它们是否在函数中。没有什么好主意,我尝试编写一个扩展为类型的宏:

// Inside of `foo`:
macro_rules! Floaty {
    () => { <C as Index<u32>>::Output };
}

Floaty!()::PI;    // errors

虽然我在这方面取得了部分成功(Floaty!() 在某些类型的上下文中有效),但最后一行错误:

error: expected one of `.`, `;`, `?`, `}`, or an operator, found `::`
  --> src/lib.rs:20:14
   |
20 |     Floaty!()::PI;    // errors
   |              ^^ expected one of `.`, `;`, `?`, `}`, or an operator here

error[E0575]: expected method or associated constant, found associated type `Index::Output`
  --> src/lib.rs:17:17
   |
17 |         () => { <C as Index<u32>>::Output };
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
...
20 |     Floaty!()::PI;    // errors
   |     --------- in this macro invocation
   |
   = note: can't use a type alias as a constructor

我的尝试都没有完全奏效。 是否可以避免每次都写出完整的类型名称?

【问题讨论】:

    标签: rust type-alias


    【解决方案1】:

    Diesel 也有类似的“问题”,他们已经通过defining non-function-local type aliases 解决了这个问题。我喜欢这个解决方案,因为你也可以使用别名来清理你的特征边界:

    type Floaty<C> = <C as Index<u32>>::Output;
    
    fn foo<C>(container: &C)
    where
        C: Index<u32>,
        Floaty<C>: Float,
    {
        let p = Floaty::<C>::PI;
        // ...
    }
    

    请注意,您必须更改您的特征 Float 以要求它是 Sized 才能实际运行此代码。

    【讨论】:

      【解决方案2】:

      我看到这样做的唯一方法是将类型作为另一个类型参数添加到函数中。

      fn foo<F, C>(container: &C)
      where
          F: Float,
          C: Index<u32, Output = F>,
      {
          let pi = F::PI;
          // ...
      }
      

      这通常不会导致类型推断问题,因为只有一种类型 F 适用于给定的 C(至少在本例中),但它确实使某些用途更加嘈杂,因为指定F 类型,您还必须为C 放置一个占位符,反之亦然。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-14
        • 1970-01-01
        • 1970-01-01
        • 2021-11-06
        • 1970-01-01
        • 2016-10-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多