Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a function-local type alias of the function's type parameters (or their associated types)?

I have a generic function foo with some complex-ish trait bounds:

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,
{
    // ...
}

I now need to use the type <C as Index<u32>>::Output inside the function a bunch (e.g. to get π via ::PI or say ::from_f32(3.0)). But this type is long to type out by hand and makes the whole code very verbose and hard to read. (Note: in my real code, the actual type is even longer and more ugly.)

To solve this, I tried to create a function local type alias:

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

But this results in this error:

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

So, just like other items, type aliases are also treated regardless of whether they are in a function or not. Not having any good ideas, I tried to write a macro that expands to the type:

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

Floaty!()::PI;    // errors

While I had partial success with this (Floaty!() is valid in some type contexts), this last line errors with:

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

None of my attempts worked completely. Is it possible to avoid writing out the full type name each time?

like image 447
Lukas Kalbertodt Avatar asked Jan 27 '23 18:01

Lukas Kalbertodt


1 Answers

Diesel has a similar "problem" and they've solved it by defining non-function-local type aliases. I like this solution because you can use the alias to clean up your trait bounds as well:

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;
    // ...
}

Note that you'll have to change your trait Float to require that it's Sized in order to actually run this code.

like image 163
Shepmaster Avatar answered Jan 30 '23 01:01

Shepmaster