Impl blocks inside funcs?

How this code works while it was supposed to be failed?
Even if it works , expectations are to be scope wise associated functions from my side tho

Quotation first:

Function Syntax
Function :
FunctionQualifiers fn IDENTIFIER GenericParams?
( FunctionParameters? )
FunctionReturnType? WhereClause?
( BlockExpression | ; )

And for BlockExpression

BlockExpression Syntax
BlockExpression :
{
InnerAttribute*
Statements?
}

Statements are usually required to be followed by a semicolon, with two exceptions:

  • Item declaration statements do not need to be followed by a semicolon.
  • Expression statements usually require a following semicolon except if its outer expression is a flow control expression.

Explanation:

Item declarations are allowed in block expressions, which seems trivial, but actually
plays an important role in macros, since it offers a small scope without messing up your code context. Const items allow block expressions too, so you'd have the Serialize impl on your type

#[derive(serde::Serialize)]
struct A;

#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () =
    {
        #[allow(unused_extern_crates, clippy :: useless_attribute)]
        extern crate serde as _serde;
        #[automatically_derived]
        impl _serde::Serialize for A {
            fn serialize<__S>(&self, __serializer: __S)
                -> _serde::__private::Result<__S::Ok, __S::Error> where
                __S: _serde::Serializer {
                _serde::Serializer::serialize_unit_struct(__serializer, "A")
            }
        }
    };
2 Likes

no, it's not supposed to fail. as explained by @vague

your expectations are off. methods, or associated functions, are associated items:

https://doc.rust-lang.org/stable/reference/items/associated-items.html

associated items are, as their name suggested, associated with the implementing type, so the visibility is associated with the type, not the impl block.

impl blocks don't have a canonical qualified path, but associated items do have canonical path, as long their associate type has canonical path:

https://doc.rust-lang.org/stable/reference/paths.html#canonical-paths

3 Likes

Hey @vague , thanks for your time

I actually know that it's correct by syntax but semantically it seems vague
Like i declared a type on global scope and i implemented a function on it inside local scope of some function implement ( surprisingly it even worked without calling it )
So , assumptions would be

  • impl block must lie in the scope of type declaration
  • Even if it's allowed irrespective of scopes , it must associate the implemented function for that particular scope i guess...

Above statements are my personal opinions which aren't intended to hurt Rust's design philosophy tho but I can't relate with current mechanism

Welp , seems like it but i may need to check them further
Thanks for your time tho @nerditation

So what is the scope of type declaration? Does it mean all impl blocks are only allowed in the module that type is declared in? If so, this would prevent someone from splitting some impls into separate modules where the type isn't defined there and cause a long file and much inconvenience; also, you've never have all kinds of trait impls like blanket impls or ones where local traits are implemented for outer types.

In this case, it's declared globally.

Its implied so yeh

I see , this seems valid argument
I didn't reached that level yet to get this since i am learning rust from the reference book and asking my doubts on the go.

impl blocks (formally, inherent implementations) must be defined within the same crate as the implementing type. that's all.

the visibility of an item depends on it's path, you can define regular (non-associated) items like functions or structs within a block, and these items indeed have scoped visibility, because (quote from the "canonical paths" section of the reference):

Items defined in block expressions do not have canonical paths.

but for associated items, their canonical path is prefixed by the implementing type, not by the implementation items themselves (i.e. the impl blocks). (to quote the "inherent implementations" section of the reference):

The path to an associated item is any path to the implementing type, followed by the associated item's identifier as the final path component.

specifically, the visibility for an associated item can be controlled using keywords, including default or private (i.e. only same module), pub(super), pub(crate), pub(in ::absolute::module::path), or just pub, but there's no such things as pub(fn) or pub(block) or pub(scope) or the like.

2 Likes

Note that there is an about-to-be-accepted RFC which plans to deprecate and eventually remove (in future language editions) the ability to do this — not because of any semantic problem with what it means, but because needing to scan the entire source code to find related impl blocks is a burden on both humans and development tools.

An impl Point block will still be allowed in a function body if struct Point is also declared inside the same function.

4 Likes

Hey @kpreid , Thanks for your time too

Sounds good news to me

Welp , atleast it was noticed somehow