Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Self-calling functions; usecase: macros #15854

Closed
justanotheranonymoususer opened this issue Jul 21, 2014 · 7 comments
Closed

Self-calling functions; usecase: macros #15854

justanotheranonymoususer opened this issue Jul 21, 2014 · 7 comments

Comments

@justanotheranonymoususer

I've created a discussion topic on Reddit, but for some reason it disappeared from the Rust page, so I'm filling an issue here. If there's a better place to discuss similar suggestions, please tell me.

I was just playing with Rust on http://rustbyexample.com/, and I've accidentally discovered that writeln can be called without a semicolon.
It doesn't look like a big deal, but for a perfectionist like me, I see it as a consistency flaw, especially for (probably) the most common macro in the language.

The author of Rust by example explained what's going on here, but that doesn't look right to me. Even C has a do-while(0) hack for this case.

This is an expanded println!("test"). The match looks very similar to the do-while(0) C hack for macros, and I think that it's the wrong way to do it, not only because it looks and feels like a hack, but also because it behaves differently than a native Rust function. For example, println!("test") println!("test") compiles, while returns_unit() returns_unit() doesn't.

My proposal: use a self-calling function, similar to what JavaScript allows. I don't know whether it's currently possible in Rust, but if not, I think it could be a natural addition to the language. Here's how I expect it to look, more or less.

@huonw
Copy link
Member

huonw commented Jul 21, 2014

I've created a discussion topic on Reddit, but for some reason it disappeared from the Rust page, so I'm filling an issue here. If there's a better place to discuss similar suggestions, please tell me.

(Posts and comments with is.gd links get immediately killed by the reddit spam filter, I've approved your post for now, and added some hints about avoiding the spam filter to the end of the sidebar.)


This behaviour is not similar to the C do while hack, and isn't at all related to the syntactic form to which they expand. If you drop a semicolon on a "statement-y thing" (a macro invocation, an if or a match) the compiler enforces that the return type is (), e.g. this is ok:

#![feature(macro_rules)]

fn returns_unit() { }

macro_rules! function( () => { returns_unit() } )

fn main() {
    function!()
    function!()
    function!()
}

(But fn returns_u8() -> u8 { 0 } would not, without adding more semicolons.)

The only reason the match is being used is because:

  • println! should only 'evaluate' each argument once (it's a syntactic transformation which can duplicate AST nodes, causing things to be called twice),
  • a reference needs to be taken, to avoid moving variables,
  • any rvalue variables (e.g. the foo() in println!("{}", foo())) needs to live long enough for all the manipulation that is being done.

Macros are proper AST items, and cannot expand to things like a half statement or a partial declaration, e.g. a macro cannot expand to just if x, it has to include the { ... } part of the code. Furthermore, the following doesn't work

macro_rules! if_true { () => { if true {} } }

// try to merge the `else` into the `if
if_true!() else {}

i.e. the contents of a macro are truly contained (other than any actual item definitions they introduce, but theoretically hygiene means these are also implied by each invocation).

I think this semicolon behaviour is more to give a form of consistency for macros that define things (One doesn't have to put ;s after nested functions, or other nested item definitions) and for those that act as control flow (one can drop ; after if, match, while etc. with the type restriction noted above), e.g.

fn main() {
    macro_rules! foo {
        (...) => { ... }
    }    

    if true { foo!(1, 2) } else { foo!(3, 4); bar() }
}

In any case. I fail to see how a self invoking function is any nicer than just expanding to a normal { ... } block (or a match if the specific temporary variable requirement noted above is relevant, which it isn't for most macros).

@justanotheranonymoususer
Copy link
Author

In any case. I fail to see how a self invoking function is any nicer than just expanding to a normal { ... } block (or a match if the specific temporary variable requirement noted above is relevant, which it isn't for most macros).

My intention was so that println!("...") will behave the same way as returns_unit().

e.g. here we have:

    fn () {
        // implementation
        ::std::io::stdio::println_args(&__args)
    }()

This is a function call, so it must be followed by a semicolon, the same way as returns_unit(). But if println is the last command in the scope, it's used as a return value, just like with returns_unit().

I'm just learning Rust, so I don't know well how macros work yet, but I hope that my intention is clear. Does it make sense?

@huonw
Copy link
Member

huonw commented Jul 21, 2014

As I said above, whether a ; is required is not related to what the macro expands (i.e. the compiler doesn't care about what function invocations or loops or whatever you have inside your macro), but rather to the type of the expanded result. Or, to put it another way, if a macro is invoked without a ;, then the type checker ensures the result of the macro is () (if it is an expression).

So it sounds a little like your proposal also requires changing the semicolon rules, to be based on the actual AST structures to which the macro expands. (Rather than just imposing the type requirement of the expanded result, as is currently done.)

Also, in Rust, an anonymous functions don't really work like that:

  1. it need a closure, a fn is just a bare function, with no captured variables,
  2. it needs to be avoid our current || closures, due to not being able to move out of variables (because of compulsory capture by-reference) and non-guaranteed performance (due to dynamic dispatch); both are addressed by the nearly complete 'closure reform', but...
  3. even with the reformed closures, it would need to have some form of by-value/by-reference inference, because some variables will be consumed in the macro invocation and others will not.

In JS, all functions are closures implicitly, and neither of 2 or 3 apply: there's no language-level alternative to the dynamic dispatch (and Rust is aiming at a space where always-dynamic is not justifiable) and there's no concept of by-value/by-reference or moving of variables.

@justanotheranonymoususer
Copy link
Author

Thank you for the response. I don't think I have enough background to continue the discussion. I was just bothered by the inconsistency of functions vs macros, and tried to propose a solution without actually understanding how things work.

If you think that my suggestion is invalid, feel free to close the issue.

@sinistersnare
Copy link
Contributor

@justanotheranonymoususer You raise a valid concern, usability for beginners.

Having semicolons sometimes and sometimes not is strange. Even if it is technically correct, it is still ergonomically weird.

@steveklabnik
Copy link
Member

Given that this kind of change would require an RFC, and it's not likely to be changed without serious work on said RFC, I think we should just close this issue.

@steveklabnik
Copy link
Member

I'm pulling a massive triage effort to get us ready for 1.0. As part of this, I'm moving stuff that's wishlist-like to the RFCs repo, as that's where major new things should get discussed/prioritized.

This issue has been moved to the RFCs repo: rust-lang/rfcs#728

bors added a commit to rust-lang-ci/rust that referenced this issue Nov 16, 2023
…lnicola

fix: Ignore doc(hidden) attr if no body is present

fixes rust-lang#15782
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants