Как стать автором
Обновить

Комментарии 18

Перейдя в директорию notes_list, можно убеться, что созданный нами шаблонный проект собирается и запускается:

То есть, когда я указываю &mut для аргумента, его тип меняется? Был &Vec<String> - указатель на read-only список, а будет указатель на модифицируемый список &mut Vec<String>?

И в первом случае методы по добавлению будут недоступны?

Да, все верно

error[E0596]: cannot borrow `*notes` as mutable, as it is behind a `&` reference
 --> src/main.rs:8:5
  |
7 | fn add_note(notes: &Vec<String>) {
  |                    ------------ help: consider changing this to be a mutable reference: `&mut Vec<String>`
8 |     notes.push("test".to_string());
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `notes` is a `&` reference, so the data it refers to cannot be borrowed as mutable

Есть ли смысл вместо ссылки на вектор использовать ссылку на слайс?

fn show_notes(notes: &[String]) {
}

// или даже так
fn show_notes(notes: &[&str]) {
}

Второе - не всегда, т.к., чтобы получить &[&str], нужно иметь что-то типа Vec<&str>, его нельзя сделать напрямую из Vec<String>. Первое - да, всегда лучше, чем &Vec<String>, поскольку с точки зрения API эти два типа отличаются только возможностью узнать capacity вектора, но при этом при использовании среза у нас на один уровень указателей меньше.

но при этом при использовании среза у нас на один уровень указателей меньше.

Дело даже не в этом. Использование &Vec<String> в сигнатуре принуждает вызывающую сторону выделять память в хипе под вектор, а вот с &[String] можно сконструировать массив строк на стеке и отдать ссылку на него.

Как верно написал , просто так рассматривать &Vec<String> как &[&str] не получится. Причина в том, что слайс &[T] подразумевает последовательно идущие данные типа T (с учётом выравниания), в то время как у String представление включает в себя &str, но не только его, из-за чего сугубо такой варинт без копирования (в новую структуру с новым представлением) не реализуем.

Но есть гораздо более гибкий вариант сделать метод более универсальным без необходимости в лишних копированиях, к которому мы придём в несколько шагов улучшения изначального метода:

  1. пусть вместо строго строки (а, фактически, нас волнует даже не String, а то, что мы получаем ссылку ан её str-составляющую) будет абстрактное нечто, что можно преобразовать в &str:

fn show_notes<T: AsRef<str>>(notes: &Vec<T>) {
    // Выводим пустую строку.
    println!();

    // Для каждой заметки в заметках ...
    for note in notes {
        // выводим её на экран.
        println!("{}", note.as_ref())
    }
}

Я метода появился типовой параметр T, который должен быть чем-то, что может быть представлено как ссылка на str.

  1. на место вектора действительно можно поставить просто слайс (фактически, для вектора Vec<T> верно, что он AsRef[T], но тут в этом нет необходимости):

fn show_notes<T: AsRef<str>>(notes: &[T]) {
    // Выводим пустую строку.
    println!();

    // Для каждой заметки в заметках ...
    for note in notes {
        // выводим её на экран.
        println!("{}", note.as_ref())
    }
}
  1. наконец, вво имя красоты и компактности, воспользуемся синтаксическим сахаром, позволяющим описать п.1 более компактно:

fn show_notes(notes: &[impl AsRef<str>]) {
    // Выводим пустую строку.
    println!();

    // Для каждой заметки в заметках ...
    for note in notes {
        // выводим её на экран.
        println!("{}", note.as_ref())
    }
}

По итогу, имеем метод, который принимает слайс, содержащий что-то, что можно представить как ссылку на str, работающий как со String, так со str, так и с произвольными типами, реализующими AsRef<str> (при этом лежащими в любом контейнере, который представим как слайс):

fn main() {
    let notes = vec!["foo".to_string(), "bar".to_string()];
    show_notes(&notes);

    let notes = vec!["baz", "qux"];
    show_notes(&notes);

    let notes = vec!["dora", "prgrm"];
    show_notes(&notes);
}
  1. В качестве более продвинутого варианта, можно принимать даже не слайс (потому что непосредственной необходимости в последовательности данных у нас нет), а нечто, что можно последовательно обходить (итерироваться по этому), или даже то, почему можно устроить обход:

fn show_notes(notes: impl IntoIterator<Item = impl AsRef<str>>) {
    // Выводим пустую строку.
    println!();

    // Для каждой заметки в заметках ...
    for note in notes {
        // выводим её на экран.
        println!("{}", note.as_ref())
    }
}

за счёт чего будет ещё большая гибкость:

let mut notes = HashMap::new();
notes.insert("a", "x");
notes.insert("b", "y");
show_notes(notes.keys());
show_notes(notes.values());

Разумеется, это не следует из этой статьи, поскольку в ней трейты не рассматривались, но, с точки зрения общего развития (и заманивания в секту Раста :) ) считаю это достаточно интересным.

Если интересен вопрос производительности, то, де факто, компилятор выполнит мономорфизацию, а именно создаст реализацию метода под каждый использующийся с ним тип (не каждый потенциально доступный, а именно каждый, который реально используется в программе), то есть, фактически, для оригинального варианта там будет всё тот же &Vec<String>, для других &[String], &Vec<&str> и так далее.

std::io::stdin().read_line(&mut buffer).unwrap();

Что здесь обозначает вызов read_line я понял, и наверное поймут большинство знающих какой либо другой язык, объяснять не надо. А вот что такое unwrap() и зачем он тут нужен, совершенно не ясно, и в подробном разборе стоило бы упомянуть, а разъяснений не вижу.

Действительно. Я даже продумал текст на эту тему, а добавить забыл(

Добавлю сейчас.

Готово. Теперь есть короткое описание того, как, в данном случае, используется механизм обработки ошибок в Rust.

Ага я уже нашел на stack overflow, но все равно спасибо, хорошее объяснение.

Очень понравился подобный формат статьи, будучи совсем новичком в Расте обнаружил для себя кучу полезного.

Вот если бы еще тему GUI затронуть, и немного ООП на простых, но реальных примерах, да чтоб еще и с зачатками каких либо паттернов проектирования - счастью не было бы предела.

Да, я планировал, если статья окажется полезной, продолжить в том же духе. Возможно, развивая далее приложение для заметок.

К doc.rust-lang.ru/book есть еще github.com/rust-lang/rustlings — цикл задач, привязан к учебнику и на практике позволяет попробовать то, что только что прочитано.

В таких статьях мне всегда не хватает раздела который бы рассказывал как на это все написать тесты

Зарегистрируйтесь на Хабре, чтобы оставить комментарий