Not understanding namespacing (correct usage of :: )

I started with Rust around two weeks ago. I am now in chapter 6 of the book. And I think, I identified one general aspect, which us not clear for me and which makes my progress dificult. (Please, forgive me my unclear english especially according to programming terminology.)

First - I have some (pretty outdated) experience with LPC programming language, which is of C family. I didn't need to take care about namespaces there. (I mean :: ). Actualy - it was used, but only in case there was doubleinheritance of a function (function was defined in more parent objects) or I wanted to override a function, but needed to keep also functionality of this function from parent object.

In Rust I understand that I need to use "full path" to get to the desired function (or method). But already in Guessing game there are points I do not fully grasp.

use std::io; io::stdin().read_line(&mut guess).expect("Failed to read line");

I am comfortable with "stdin()" function being part of "io" module, which is part of "std" crate. But:

is read_line() method (or "handle", whatever does it mean) part of stdin() function? And expect() part of read_line()? I know, this will be explained later, but then I run into this:

use rand::Rng;
let secret_number = rand::thread_rng().gen_range(1, 101);

Why there needs to be Rng, when it is not used in syntax. I would understand following syntax:

use rand;
let secret_number = rand::thread_rng().gen_range(1, 101);

I can live even with this to later stages, but then I found myself struggling with basic aspects like this:

Why there are two different ways to access values in array on one side and values in tuple on other side:
my_array[1] vs. my_tuple.1. I am able to remember correct syntax, but I don't understand, why is it different.

And especially structs:

struct Rectangle { x: u32, y: u32 }

impl Rectangle {
  fn area(&self) -> u32 {
    self.x * self.y
  }
  fn square(n: u32) -> Rectangle {
    Rectangle {x:n, y:n }
  }
}

fn main(){
  let rect1 = Rectangle {x: 20, y:30};
  let area = rect1.area();  
  let rect2 = Rectangle::square(10);
}

Why cannot I write Rectangle.square(10)? I understand, dot is used to access instance of Rectangle and :: is used when there is no instance. I can remember correct syntax, but I need to understand why is it done this way.

I think, I am unable to dive deeper into the language unless I understand this basic stuff. Please, can you help me out or give a link to explanations? Thank you very much.

1 Like

Note that the syntax for a code block is a triple backtick. > is for quotes

```
// your code here
```

In your first example:

In this case the stdin function returns a value of type Stdin (notice capitalization). Values of this type has a method called read_line, which is then called on the value returned from stdin. The read_line method then returns a Result, and the unwrap method is then called on the result.

Then we get to the trait method

In this case you start out by calling the thread_rng function. This returns a value of type ThreadRng. There are no methods implemented directly on this type, but if you look in the "Trait Implementations" section you will see that it implements the trait RngCore.

However it turns out that the rand crate provides a blanket implementation that says that everything implementing the RngCore trait also implements the trait Rng. You can find the blanket implementation here.

So we now have a value of type ThreadRng, which implements the trait Rng. This means you can call all methods on the Rng trait on values of type ThreadRng. However you have to import the trait to call the methods, so since gen_range is defined on Rng and not ThreadRng, you need to import the trait to call the method.

Well accessing something with the indexing syntax my_array[1] is a function call (it's this method to be precise), whereas accessing a value in a tuple with my_tuple.1 is accessing a field. It's just like accessing the x field in your Rectangle struct with my_rect.x. The compiler will complain if you try to access a field that doesn't exist, but trying to access the tenth value in a five-length array will not fail until you actually run it.

When writing my_rect.area(), it's just syntax sugar for Rectangle::area(&my_rect). You can think of Rectangle as a module that has all the functions you defined for that type. The dot syntax is just a shorthand for figuring out where the function at hand is defined (it might be on a trait), and calling it with the value as first parameter (the self parameter).

Note that had you defined your struct like this

struct MyStruct;

Then you could totally write MyStruct.foo() as that would simply mean creating a value of MyStruct (it has no fields so that's easy), and calling foo on that value. If you could also write Rectangle.square(10), then it would be ambiguous. What if MyStruct had a square function too? Then MyStruct.square(10) would be a fundamentally thing different from MyStruct.foo().

2 Likes

Thank you for quick response.

Done, thank you :slight_smile:

This is important part of your answer, it clarifies and helps a lot.
The rest I will need to read again :slight_smile:

1 Like

Though I still do not fully understand details of this particular case, I think I've understood the general approach. Fantastic. Similar to other parts :slight_smile: Thank you.