Names in Rust - a Survey (Comments Very Welcome)

In this post I would like to discuss names in Rust: constants, statics, independent functions, types, associated functions, methods, enum variants, and field names (Have I left out anything?). I now give a simple program to illustrate the above.

mod moda {
    pub const ZERO:i32 = 0;
    pub static UNITY:i32 = 1;
    pub fn increment(r: &mut i32) {
        *r += 1;
    }
}

mod modb {
    pub struct Point {pub x:i32, pub y:i32}
    impl Point {
        pub fn new()->Point {
            Point{x:0, y:0}
        }
        pub fn mirror(&mut self) {
            self.x = -self.x;
            self.y = -self.y;
        }
    }
    pub enum Dir {Left, Down, Right, Up}
}

fn main() {
    let z = moda::ZERO;
    let mut u = moda::UNITY;
    moda::increment(&mut u);
    let mut p = modb::Point {x:2, y:-3};
    let q = modb::Point::new();
    p.mirror();

    println!("{z} {u} ({},{}) ({},{})", p.x, p.y, q.x, q.y);
    println!("{} {} {} {}", modb::Dir::Left as i32, modb::Dir::Down as i32,
    modb::Dir::Right as i32, modb::Dir::Up as i32);
}

Any name begins with a module path (can be empty if in the same module), then followed by other types of names. I also found the following rules (correct me if there are any errors):

  1. Constants, statics, independent functions must immediately follow a module name and then end (i.e. cannot be followed by anything like ::name)
  2. Types (including structs, enums and other type names) must immediately follow a module name but can have a further level of branch names (i.e. followed by something like ::name ).
  3. Associated functions, methods must follow a struct or enum type name and then end.
  4. Enum variants must immediately follow a enum name and then end.
  5. Field names need not begin with a path, because they always follow a variable or constant name, which already has the path imformation.

Look at the above rules (especially 1 and 3), it may seem like we are not allowed to define types or functions within a function. But actually it isn't so. We can! It is only that we can only refer to names that are defined in functions in the SAME or an ancestor scope. Let us see an example:

// This snippet does not compile!
fn myfun() {
    pub const ZERO:i32 = 0;
    pub fn increment(r: &mut i32) {
        *r += 1;
    }
}

fn main() {
    let mut z = myfun::ZERO;
    increment(&mut z);
}

Rustc gives the following error info:

Now let revise the code to use the names in the same scope or an ancestor scope (parent scope in the code).

fn main() {
    fn sub() {
        let z = ZERO;
        let mut u = UNITY;
        increment(&mut u);
        let mut p = Point { x: 2, y: -3 };
        let q = Point::new();
        p.mirror();

        println!("{z} {u} ({},{}) ({},{})", p.x, p.y, q.x, q.y);
        println!(
            "{} {} {} {}",
            Dir::Left as i32,
            Dir::Down as i32,
            Dir::Right as i32,
            Dir::Up as i32
        );
    }

    sub();
    const ZERO: i32 = 0;
    static UNITY: i32 = 1;
    fn increment(r: &mut i32) {
        *r += 1;
    }
    struct Point {
        x: i32,
        y: i32,
    }
    impl Point {
        fn new() -> Point {
            Point { x: 0, y: 0 }
        }
        fn mirror(&mut self) {
            self.x = -self.x;
            self.y = -self.y;
        }
    }
    enum Dir {Left, Down, Right, Up}
}

The program compiles just fine and produces the desired result!

PS: It seems to me that the above findings have not yet been properly documented in any book or web material. If I am wrong, could you please give me any pointer? Also, please correct me if there is any inaccuracy and help me improve this post. Thank you very much!

It sounds like you haven't heard of the concept of items. Items are, loosely speaking, “the things you can put in modules”. (They can also appear as associated items or inside blocks, but only items appear in modules.) If you restate your rules in terms of items, they should be expressible much more concisely.

Also check the Names section of the reference, with its chapters on paths and name resolution.

This is because all of these things are members of the value namespace, and values never appear on the left side of ::. The same logic applies to associated functions and enum variant names.

I believe that in general, the thing on the left side of an :: is always something from the type namespace.

Types (including structs, enums and other type names) must immediately follow a module name

There are also associated types, where the path component before the type name is a trait or type, or the path is a qualified path which specifies both a trait and a type.

  1. Field names need not begin with a path, because they always follow a variable or constant name, which already has the path imformation.

Field names cannot be used via paths. They're a unique case; they appear only in field expressions.

4 Likes

Everything related to semantic and syntactic details should be in The Reference.


As for why you can't poke into the body of a function: well why should you be able to? A function body is the function's own business, it's the pinnacle of implementation detail. Changing a function body without changing the signature should never cause code to fail. That would be disastrous.

4 Likes

Thank you kpreid! I'll look into the Rust Reference and come back a few days later.

Thank you paramagnetic! I'll look into the Rust Reference and come back a few days later.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.