Серия глупых вопросов от новичка

Привет, я совсем новичок, пришел из JS, буду тут задавать свои вопросы. И испытывать чувство благодарности и посылать вам лучи добра за ответы.
Заранее большое спасибо.

Начну с самых верхних строчек кода из книжки:

Подскажите пожалуйста, что на самом деле означает двойное двоеточие :: ? Чем оно отличается от точки?

use std::io::stdin;

Это значит, что есть объект std, в нем io, а в io есть stdin у которого есть метод read_line? Что это за объекты, какие между ними отношения?
Почему в коде мы пишем со скобками: stdin().read_line(), а не stdin.read_line() - скобки указывают на функцию/метод?
Какова иерархия объектов?

use rand::Rng;

зачем нужен Rng, почему с большой буквы?
Потом в коде мы обращаемся непосредственно к rand:
let secret_num = rand::thread_rng().gen_range(1, 101);

Почему если написать
use rand::Rng::thread_rng();
и в коде: thread_rng().gen_range(1, 101);
по аналогии с use std::io:stdin;
оно не будет работать?

Спасибо!

Добро пожаловать!
Прежде всего, мы регулярно мониторим форум и ответим на любые вопросы, но большая часть сообщества сидит в нашем Gitter-канале ruRust. Простые вопросы можно задавать в комнате easy.

Теперь к вопросам.
Двойное двоеточие служит для указания областей видимости (в частности модулей).
В Rust, в отличие от многих других языков, не существует понятия “объект”. Вместо этого для изоляции логики и построения абстракций используются модули, а для хранения данных - структуры.
Подробнее о модулях можно почитать в официальной документации (тут)
Таким образом, std - это модуль (стандартная библиотека), io - подмодуль std, содержащий функции для работы с вводом-выводом. stdin - это функция, из модуля io. Получается, этой строчкой мы импортируем одну единственную функцию из модуля io стандартной библиотеки.
Именно поэтому в коде нужно писать stdin(), а не stdin - скобки служат указанием, что перед нами - функция (если не ошибаюсь, в JS точно так же).

Rng - это трейт из модуля rand. Что такое трейты можно узнать из книги (тут)
Трейты принято именовать, используя CamelCase.
Импортируем трейт мы для того, чтобы использовать его функции.

Почему если написать
use rand::Rng::thread_rng();
и в коде: thread_rng().gen_range(1, 101);
по аналогии с use std::io:stdin;

Здесь вы пытаетесь импортировать функцию трейта (это невозможно, да и функции такой нет). Функция thread_rng() хранится в модуле rand, поэтому мы используем ее как rand::thread_rng().
Чтобы сократить код, можно ее предварительно импортировать: use rand::thread_rng;, а затем использовать без указания модуля: thread_rng().

Надеюсь было чуточку понятно, не стесняйтесь задавать вопросы.

3 лайка

Спасибо!

use std::fs;

fn main() {
   let paths = fs::read_dir("./").unwrap();
   let x = paths.count();

  for path in paths {
      println!("Name: {}", path.unwrap().path().display())
   }

}

Почему произошла передача владения? я же только вызываю метод сущности paths (верно?). Или в контексте владения это аналогично get_length(paths)? Т.е. здесь мне нужна ссылка?

Но и let x = &paths.count(); не работает:

use of moved value: paths

Потому что метод может “проглатывает” объект и вместо него выдавать что-то,
например

struct Foo;
impl Foo {
    fn method1(&self) {}
    fn method2(self) {}
}

Как видите method1 в качестве параметра принимает &self,
а вот method2 self, т.е.

let foo = Foo;
foo.method1();//все хорошо
foo.method1();//все хорошо
foo.method2();//все хорошо
foo.method1();//ошибка компиляции, method2 уничтожил объект foo

В вашем случае метод итератора count проглатывает объект,
(из-за того, что внутри он его модифицирует),
поэтому после let x = paths.count(); paths в никаком виде использовать
нельзя, такого объекта просто нет. Нужно его пересоздать.

В принципе метод count() можно вызвать по ссылке, но ссылка и сама переменная paths должны быть мутабельными:

    let mut paths = fs::read_dir("./").unwrap();
    let x = (&mut paths).count();
    println!("x = {}", x);

Более удобный способ сделать тоже самое:

    let x = paths.by_ref().count();

Если после этого запустить цикл по paths:

  for path in paths {
      println!("Name: {}", path.unwrap().path().display())
   }

он ничего не выведет так как метод count() проматывает итератор в конец. Перемотать его обратно нельзя.

Для итераторa ReadDir типаж Clone не реализован, так что единственный способ пройтись по путям после вызова метода count() - создать новый итератор.