`fn self` in libs/modules


#1

A lib.rs should be able to contain fn self() {}.

e.g.

// lib.rs
fn self<T>() -> T {
    Default::default()
}

usage (rust 2018):

use default;

let foo: u32 = default();

#2

…and what would fn self() { /* ... */ } do exactly, other than defining a function named self to call?


#3

the module/crate would become a function


#4

Why would that be better than this?

use default::default;

let foo: u32 = default();

#5

It avoids the stutter, which is advised against in the Rust API guidelines.

(I am mostly neutral on this.)

Would you still be able to have other items in a module that declares a self item? Is this self item restricted to being a fn, or can it be any item?


#7

You could just as easily avoid stutter by using a different module name. Contrast this with the fact that a similar feature does not exist for crates, so having a crate with a same-named function in it naturally stutters. (A connect crate with a connect fn inside it would not be a totally wacky thing, but it would still stutter.)


#8

self or Self, and it ends all other items (unless we plan on adding Lua-like metatable-like stuff and fn self is just another special item).

e.g. these are okay:

// foo.rs
fn self(h: usize) -> usize {
}
// bar.rs
struct Self {
}
impl Self {
}

these I don’t like:

struct Foo {}
impl Foo {
fn self() -> Foo {
Foo{}
}
}
// this wouldn't compile (because it conflicts) but this is what a tuple struct would "desugar" to, sort of:
struct Bar(usize);
impl Bar {
fn self(x: usize) -> Bar {
Bar { 0: x }
}
}

from all of these struct Self is the most flexible, as it can have consts and types and fns and stuff. but idk if I like being able to overload things like this. I really just want to get rid of the useless path.


#9

I believe that this is intended to work for crates. (Note that Soni refers to lib.rs in the OP.) I believe this came up due to the discussion around making a Default::default free fn and the new default crate which exposes a single symbol, fn default<T: Default>() -> T.

@Soni, it would help if you list this context when you make a post. It really helps to be explicit and explain the details rather than rely on people to ask for required details to an underspecified, overly general proposal.


#10

there’s a thing you’re supposed to do to set context on this software, but idk how to use it. sorry.

the mention of default should’ve been a hint. do ppl not read code blocks?


#11

This is the internet. Paste a link.

Not everyone reads every single thread on the forum. Someone might have been linked to this thread from somewhere else. They might have slept since then and forgotten about it.

Also, you gain nothing by using “hints”, except potentially driving people away. You’re supposed to justify your proposal, not make other people do that research for you.

I don’t know if you intended it, but this comes across as phenomenally rude and dismissive. It implies that anyone who doesn’t immediately recognise what you’re talking about is illiterate and/or stupid.


#12

I saw the lib.rs, but for some reason my brain wanted to interpret it as mod.rs. I still don’t think stutter is problematic enough to drive language changes. Stutter can already be eliminated by convention, and it can be tolerated otherwise. I’d rather it first be linted against if there were a serious problem with it.


#13

Just to clarify: What do you mean by stutter? The part of the source code that explicitly declares the function? Or more something along the lines of self::self()?


#14

@Soni It would be helpful to you and everyone else if you refrained from language like ‘this I like, but I don’t like that’ when trying to convince others to implement a feature in a programming language.

The problem is that not only is liking something highly subjective, it is a horrendous metric for features: it accounts neither for costs (e.g. the feature’s complexity, ecosystem-wide learning costs, limits imposed on the language by the new feature) nor its potential benefits (what is gained, if anything, by adding the feature?)

Putting your feature proposal into cost/benefit terms, I see the following:

  • Benefits: Unknown. It’s 100% unclear to me what purpose such a feature could possibly serve that isn’t already being met, thus this needs to be explained more explicitly. For the record: it’s possible there is a benefit, but I haven’t a clue what it is from reading your post.
  • Costs: It’s yet another feature to learn and thus for people to be confronted by in code. It doesn’t seem extremely complex, but the indirect consequences of using this in code aren’t fully clear to me either*.

*Indirect consequences are important. To see why, consider the example of OO-style inheritance. It’s mostly used to share implementation, which is a direct consequence. An indirect consequence is that that implementation sharing leads to code that is hard to change and maintain, but you’re only confronted by those costs after a while when you’re already suffering the consequences to a fair degree, which makes it an insidious feature. That in turn is one of the major reasons why newer languages don’t add the feature anymore when they can avoid it.


#15

This is already possible to emulate in any position other than crate-root:

pub fn foo() { ... }
pub mod foo { ... }

or if you need access to module internals

pub use self::foo::this;
pub mod foo {
    pub(crate) fn this() { ... }
    pub fn bar();
}

Using the keyword self for this is problematic with what it means in use context, e.g. if the previous example was in the crate baz then

use baz::foo::self;

fn main() {
    foo();
}

would not work, self in use context only imports from the module namespace.


Personally I really like this pattern of merged module/value namespaces, I’ve used it in a crate to provide function scoped errors, bs58::decode(foo).into_vec(): Result<_, bs58::decode::DecodeError> (heh, just noticed that even though I’m using this pattern I stuttered when creating it, I should fix that in the next breaking change). If a crate was basically a single function then I would like to make the crate-root itself that function in the value namespace.

Since there is very limited applicability, I wouldn’t see any reason to have a keyword for this. An alternative would be to just have an attribute that can be applied to a single function at the crate-root

#[hoist_to_be_crate_root_in_public_value_namespace]
fn foo() { ... }

But, I’m not sold that it’s useful in enough cases to be worth implementing.


#16

To add to this, I do read every single thread on the forum, and I did immediately recall the default() thread when I read the first post of this one, but I still didn’t understand the first post. It simply didn’t explain why using self in this way would cause the default:: to magically go away somewhere else in your code.

@Soni In general, I’ve never been able to tell what you were suggesting from any of your many threads’ opening posts. You really need to provide some kind of explanation of what it is you’re actually proposing, rather than expecting all of us to read your mind and fill in 90% of the post for you, or play a game of 20 questions to get you to tell us the rest of it (which so far is what always happens).


#17

Stutter is when you use the “namespace” in the code such as fmt::Result


#18

That’s not my understanding of stutter, I’ve always known it as when you use an items parent namespace in the items name, i.e. fmt::FmtResult would be stutter, but fmt::Result is not.


#19

I’m trying to turn the namespace/path into the function.

There would be no need to use foo::bar::self because self isn’t a function name, it’s a special syntax that turns foo::bar (also known as self) into a function.

This is useful if:

  • You maintain the default crate
  • You want to put a function on its own file without adding line noise.
  • You want the compiler to check that there’s only one top-level function in that file.
  • (I don’t like the complexity of this, but it’s a trivial variation - but you’d have to explain why it works with normal structs but not tuple structs) you want your struct to be its own constructor.
  • You simply don’t like useless paths.
  • It’s not about stutter.

#20

It’s not about whether there’s a need, it’s about whether confusion is likely to result. I would find it confusing that I could use the self keyword to declare the “self” function but not import it.

I don’t disagree with any of your pros, but I do think they are not strong enough to make the feature worthwhile, in order:

  • too limited a scope (very few crates are really intended to be a single function)
  • the line noise is relatively trivial for both reading and understanding
  • already true (you can only export one item in the value namespace at a path)
  • already possible (struct Foo { ... } #[allow(some_casing_lint)] fn Foo(...) { ... })
  • restatement of points 1&2?
  • not a pro

#21

You can always use foo::bar::self but it’s using bar, just like fn self(){} refers to bar.

And you can do this with mod foo as well as crates.