如何定义函数类型参数(或其关联类型)的函数局部类型别名?

Luk*_*odt 5 rust type-alias

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

\n\n
use std::ops::Index;\n\n// This trait is just as an example\ntrait Float {\n    const PI: Self;\n    fn from_f32(v: f32) -> Self;\n}\n// impl Float for f32, f64 ...\n\nfn foo<C>(container: &C)\nwhere\n    C: Index<u32>,\n    <C as Index<u32>>::Output: Float,\n{\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我现在需要<C as Index<u32>>::Output在函数内部使用一堆类型(例如通过::PI或说获取 \xcf\x80 ::from_f32(3.0))。但这种类型手动输入很长,并且使整个代码非常冗长且难以阅读。(笔记:在我的真实代码中,实际类型甚至更长、更难看。)

\n\n

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

\n\n
// Inside of `foo`:\ntype Floaty = <C as Index<u32>>::Output;\n
Run Code Online (Sandbox Code Playgroud)\n\n

但这会导致以下错误:

\n\n
use std::ops::Index;\n\n// This trait is just as an example\ntrait Float {\n    const PI: Self;\n    fn from_f32(v: f32) -> Self;\n}\n// impl Float for f32, f64 ...\n\nfn foo<C>(container: &C)\nwhere\n    C: Index<u32>,\n    <C as Index<u32>>::Output: Float,\n{\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

所以,就像其他物品一样,type别名也会被处理,无论它们是否在函数中。没有任何好主意,我尝试编写一个扩展为以下类型的宏:

\n\n
// Inside of `foo`:\nmacro_rules! Floaty {\n    () => { <C as Index<u32>>::Output };\n}\n\nFloaty!()::PI;    // errors\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
// Inside of `foo`:\ntype Floaty = <C as Index<u32>>::Output;\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

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

\n

tre*_*tcl 5

我见过的唯一方法是将类型作为另一个类型参数添加到函数中。

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

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


She*_*ter 5

Diesel 也有类似的“问题”,他们通过定义非函数本地类型别名解决了这个问题。我喜欢这个解决方案,因为您也可以使用别名来清理特征边界:

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;
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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