I’ve seen a few threads lately trying to come up with syntaxes for unnameable entities. I figured I’d make a complete list of everything that is currently unnameable, so that we can try to deal with it all at once. This is all in absence of typeof, of course.
This is an incomplete list based off my excellent^W iffy memory. Let me know if I missed anything!
Types
- The type of a function.
fn foo(_: T) -> U { .. }
// problem: I can't name the ZST that the following types as:
let bar = foo;
// workaround:
type foo_t = impl Fn(T) -> U;
let bar: foo_t = foo;
- The type of a fn-local item (struct, enum, type, etc)
// problem: I can't name `???` outside the fn scope
fn foo() -> ??? {
struct Foo;
Foo
}
// workaround:
struct Foo;
fn foo() -> Foo {
Foo
}
- The type of a closure.
fn foo() -> impl Fn() -> i32 {
|| 0
}
// same problem as the type of a function, really
- Output of a function.
fn foo() -> i32 { 0 };
// problem: how can I name this type without naming it again?
let bar = foo();
// workaround via assoc. type
type foo_t = impl Fn<()>;
let _: foo_t = foo;
let bar: foo_t::Output = foo();
Values
- Fn-local item (fn, const, static).
fn foo() {
fn bar() {}
}
// problem: how do I name bar outside of foo?
let baz = foo::bar;
- The captures of a closure. One can argue that those can’t be made
pubthough.
let a = 0;
let b = 1;
let bar = move |x| x + a - b;
let mut baz = bar.clone();
// problem: how do I do this? (assuming the field's
// name is the same as the capture's)
baz.b = 1;
// workaround: transmute! (or ptr casts) this is unstable though
// and maybe even UB!
unsafe { *transmute::<_, *mut i32>(&mut baz).offset(1) = 1; }
Lifetimes
- The lifetime of a function’s body.
fn foo(x: T) {
// problem: what lifetime goes here? sure, we can let the compiler
// infer it but relying on inference to name a lifetime
// can lead to problems if inference isn't allowed in some palce
let foo: &'? T = &x;
}
- The lifetime of a struct.
struct MyBox<T: ?Sized> {
// problem: what lifetime goes here? I need to use `*mut T` instead,
// since I can't just say "this pointer lives for as long as this
// this struct does" (we don't have &move yet but my point stands)
ptr_to_heap: &'? move T,
}
Solutions
The following are some ideas I’ve had/seen for solving each of these.
- Type of fn:
fn fooorfoo::type. - Type of fn-local item:
(fn foo)::Foo,foo::type::Foo,foo::Foo. - Type of closure:
// introduce a new type inside the closure by ascribing the closure.
// there's a lot of unresolved problems with this, so it should probably
// be discussed in its own thread
fn foo() -> fn::Closure {
(|| 0): Closure
// or
let closure: Closure = || 0;
closure
// or maybe denote that it's a new type with special syntax?
let closure = struct Closure || 0;
}
- Fn output:
foo::return?(fn foo)::return? Could also imaginefoo::yieldfor generators. - Inner fn: same as inner type.
- Closure captures: probably should be described in its own thread.
- Fn body lifetime:
'fn. - Struct lifetime:
'self.
PS: There’s one slightly insane corner case, where it’s impossible to name a lang item (like one of the panic fns) if you don’t know what it’s actually called. Imagine the following:
#![no_std]
fn my_panic() -> ! {
__lang__::panic_fmt()
}
I don’t think this is actually useful in any way, but I figured I’d mention it.