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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With