I don’t understand something about the feature that makes it possible to coerce a tuple struct to a function as in:
struct MyType(u8);
let optional_mytype: Option<MyType> = Some(12).map(MyType);
// ^^^^^^ here, MyType is treated as a function
In the example above, no lifetimes are involved: everything is easy. However, when the structure has a generic lifetime bound, I’m having troubles defining the exact signature of the function it would coerce to.
Here’s a MRE of something I’ve attempted to do, and that doesn’t work (sandbox link):
struct MyStruct<'a>(&'a ());
// This looks, to me, like the signature the "constructor" should have
type MyStructConstructor = for<'a> fn(&'a ()) -> MyStruct<'a>;
/// Coercion problem here
fn some_code() {
let mut constructor: MyStructConstructor = MyStruct;
}
and the error given by the type-checker:
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/lib.rs:7:48
|
7 | let mut constructor: MyStructConstructor = MyStruct;
| ^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'a> fn(&'a ()) -> MyStruct<'a>`
found fn pointer `fn(&()) -> MyStruct<'_>`
And I honestly don’t understand why my constructor type alias isn’t valid, and why the type suggested by the error message is much more lax than it should be. I mean, the lifetime <'a>
of my structure should be exactly equal to that of the unit reference that is contained by the structure, not a random one.
Here‘s a more concrete (still minimal) example of what I’m actually trying to do, if having context helps you.
Thanks in advance
>Solution :
In fact, the error message might be a bit misleading. '_
does not mean any lifetime goes, it just means a lifetime I haven’t named explicitly, just as writing &()
(which is the same as &'_ ()
). Rust does that because that’s not the issue.
To understand, let’s actually annotate implicit generic lifetimes in your code:
fn some_code() {
let constructor: MyStructConstructor = MyStruct<'_>;
}
Notice how something is wrong? On the right hand side, you have something whose type is generic over a lifetime. On the left hand side, you don’t.
In fact, a more appropriate constructor signature for MyStruct
would be
struct MyStruct<'a>(&'a ());
type MyStructConstructor<'a> = fn(&'a ()) -> MyStruct<'a>;
fn some_code() {
let constructor: MyStructConstructor = MyStruct;
}
leaving the lifetime implicit — see the playground.
This compiles.
So now you may understand why MyStructConstructor
was more generic than MyStruct
: because for MyStruct
, you have to specify a lifetime to begin with, that is, the actual type is, for a given 'a
, MyStruct<'a>
. Then, with that constructor, you are only capable of building objects of type MyStruct<'a>
, given &'a ()
. On the other hand, with MyStructConstructor
, given a reference to a ()
with any lifetime, you would be able to build a MyStruct
of the same lifetime, which is more general.