This is something I’ve wanted a lot. It would help when a function returns some type unique to that function, eg. when a function has it’s own error type:
The problem is that a function currently defines neither a type nor a namespace.
fn f() {}
mod f {}
fn F() {}
struct F {}
This is valid today, and would have to become invalid if functions would be a type and/or namespace. This is common today: tuple structs define a type and a function.
One reason I want this is so that I can have the docs for my function also display the consts/types which are only relevant to that function, rather than making the user navigate to a module with the same name.
Also, we let types double as modules via inherent impls. So I don’t see why we can’t do the same thing for functions.
// Currently allowed
fn f() {}
mod f {}
macro_rules! f {}
// Currently allowed
fn F() {}
struct F {}
macro_rules! F {}
// Currently forbidden!
mod x {}
struct x {}
// Currently forbidden!
const X: i32 = 0;
fn X() {}
ISTM that allowing fns to double as modules requires either:
Making fns define modules under certain conditions (a breaking change if those conditions do not require novel syntax or attributes)
Adding a fallback mechanism to path resolution. shudder
// `fn` is in a function as `Self` is in an `impl`
// this lines up with the idea that 'fn would be the
// "fucntion body region/lifetime", as has been proposed elsewhere
fn eat_snacks() -> Result<(), fn::Error> {
pub enum Error { .. };
...
}
const MY_ERROR: fn eat_snacks::Error = ..
// or, if it's in an impl,
const MY_ERROR: MyStruct::fn eat_snacks::Error = ..
AFAICT there should be no parsing ambiguities, since fn in non-item position must be followed by ( currently (playground). That said, while I feel vaguely grossed out by the space in fn foo appearing in a path, this is already present in fn() -> T (though I think you might need to include parens for that). We could go with (fn eat_snacks)::Error but I don’t like that either.
Random thought: Another advantage to making every function/method it’s own type would be so that you can refer to them in trait bounds. eg. When I implement a trait, I should be able to relax the effects on methods like so:
struct MyConstEqType;
// Make `eq` a const fn, even though it's non-const in the trait definition.
impl PartialEq for MyConstEqType {
const fn eq(&self, other: &MyConstEqType) -> bool {
true
}
}
I could then refer to types that have const equality by referring to the eq method:
There’s a couple of proposed fixes for being unnable to name the function (disclaimer, both syntaces are mine):
Add the production like fn $path to the type grammar, that allows for explicitly naming function ZSTs. The space is obnoxious, since you’d have to write things like (fn foo)::CONST.
Add the production $expr::type to the type grammar, like a C++ decltype. This takes an expression, but instead of evaluating it, types it and returns the type.