Привет, я совсем новичок, пришел из 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()
- создать новый итератор.