- оригинал:Учебное пособие по языку программирования Rust — Как создать приложение со списком дел
- Автор/переводчик: Клаудио Рестифо/@hylerrix
- Время выпуска/время перевода: 20210104/20210129
- Примечание. Эта статья следует заСпецификация перевода freeCodeCamp, и эта статья будет включена в«Искусство дено исследований»в главе о ржавчине.
Язык Rust привлек большое внимание сообщества с момента его первого выпуска с открытым исходным кодом в 2015 году. отStackOverflowRust также является самым популярным языком программирования среди разработчиков в 2016 году, согласно опросу разработчиков на .
Rust был разработан Mozilla и определяется как язык программирования системного уровня (подобный C и C++). В Rust нет обработчика мусора, поэтому производительность очень хорошая. И некоторые из этих дизайнов часто заставляют Rust выглядеть продвинутым.
Кривая обучения Rust обычно считается сложной. Я не очень хорошо разбираюсь в языке Rust, но в этом уроке я постараюсь предоставить практический подход к некоторым концепциям, чтобы помочь вам понять его глубже.
Что мы будем строить в этом практическом уроке?
Я решил сделать приложение для списков дел нашим первым проектом на Rust, следуя давней традиции приложений JavaScript. Мы собираемся сосредоточиться на командной строке, поэтому знание командной строки является обязательным. В то же время вам также необходимо знать некоторые основы концепций программирования.
Эта программа будет работать на основе терминала. Мы будем хранить коллекцию элементов, каждый из которых имеет логическое значение, представляющее его активное состояние.
О каких понятиях мы будем говорить?
- Обработка ошибок в Rust.
- Параметры и нулевые типы.
- Структуры и импл.
- Терминальный ввод/вывод.
- Обработка файловой системы.
- Владение и заимствование в Rust.
- шаблон соответствия.
- Итераторы и замыкания.
- Используйте внешние ящики.
прежде чем мы начнем
Перед тем, как мы углубимся в тему, давайте несколько советов разработчикам с опытом работы с JavaScript:
- Rust — строго типизированный язык. Это означает, что нам нужно следить за типами переменных, когда компилятор не может определить тип для нас.
- Также, в отличие от JavaScript, Rust неAFI. Это означает, что мы должны активно печатать точку с запятой (";") после оператора, если только он не является последним оператором функции (в этом случае точку с запятой можно опустить).
;
считать это возвратом).
Примечание переводчика: AFI, автоматическая вставка точки с запятой, автоматическая вставка точки с запятой. JavaScript может быть написан без точек с запятой, но некоторые операторы также должны использовать точки с запятой, чтобы обеспечить их правильное выполнение.
Без лишних слов, давайте начнем!
Как Rust начинается с нуля
Первый шаг для начала: Загрузите Rust на свой компьютер. Скачать его можно в официальной документации Rust.НачинаяУстановите его согласно инструкции.
Примечание переводчика: через
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Установить.
В приведенной выше документации вы также найдете инструкции о том, как интегрировать Rust с вашим знакомым редактором для более удобной разработки.
В дополнение к самому компилятору Rust, Rust поставляется с инструментом —Cargo. Cargo — это инструмент управления пакетами Rust, очень похожий на npm и yarn, используемые разработчиками JavaScript.
Чтобы начать новый проект, перейдите в терминал, где вы хотите создать проект, затем просто запуститеcargo new <project-name>
для начала. В моем случае я решил назвать свой проект «todo-cli», поэтому у меня была следующая команда:
$ cargo new todo-cli
Теперь переключитесь во вновь созданный каталог проекта и распечатайте список его файлов. Вы должны увидеть в нем эти два файла:
$ tree .
.
├── Cargo.toml
└── src
└── main.rs
В оставшейся части этого руководства мы в основном сосредоточимся наsrc/main.rs
файл, поэтому откройте файл напрямую.
Как и во многих других языках программирования, в Rust есть основная функция, которая служит точкой входа для всего.fn
объявить функцию во времяprintln!
середина!
символ представляет собоймакрос(макро). Вы, наверное, сразу увидите, что это ""Hello World
"программа.
Чтобы скомпилировать и запустить эту программу, вы можете напрямуюcargo run
.
$ cargo run
Hello world!
Как читать аргументы командной строки
Наша цель состоит в том, чтобы наш инструмент CLI принимал два параметра: первый параметр представляет тип выполняемой операции, а второй параметр представляет объект для работы.
Мы начнем с чтения и печати параметров, введенных пользователем.
Используйте следующеезаменятьОтбросьте содержимое основной функции:
let action = std::env::args().nth(1).expect("Please specify an action");
let item = std::env::args().nth(2).expect("Please specify an item");
println!("{:?}, {:?}", action, item);
Давайте вместе разберем важную информацию в коде:
-
let
[документация]привязать значение к переменной -
std::env::args()
[документация]это функция, импортированная из модуля env стандартной библиотеки, которая возвращает аргументы, переданные ей при запуске программы. Поскольку это итератор, мы можем использоватьnth()
для доступа к значению, хранящемуся в каждом месте. Позиция 0 ведет к самой программе, поэтому мы начинаем чтение с первого элемента, а не с нулевого. -
expect()
[документация]ЯвляетсяOption
Метод, определенный перечислением, вернет значение, которое необходимо указать.Если данное значение не существует, программа будет немедленно остановлена, и будет напечатано указанное сообщение об ошибке.
Поскольку программы можно запускать без аргументов, Rust просит нас проверить, действительно ли значение предоставлено, предоставив нам тип Option.
Как разработчики мы несем ответственность за принятие соответствующих мер в каждом случае.
В нашей текущей программе, если аргументы не предоставлены, программа немедленно завершится.
Давайте запустим программу с помощью следующей команды при передаче двух параметров, не забудьте добавить параметры в--
Позже.
$ cargo run -- hello world!
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running target/debug/todo_cli hello 'world'\!''
"hello", "world!"
Как вставлять и сохранять данные, используя пользовательский тип
Давайте подумаем о том, чего мы хотим добиться в этой программе: иметь возможность читать аргументы, введенные пользователем в командной строке, обновлять наш список дел и хранить его где-нибудь, чтобы обеспечить запись.
Для этого мы реализуем пользовательские типы, которые подходят для нашего бизнеса.
Мы будем использовать Rust вstruct(struct), что позволяет разработчикам проектировать код с лучшей структурой, избегая, таким образом, необходимости писать весь код в основной функции.
Как определить нашу структуру
Поскольку мы будем использовать много HashMap в нашем проекте, мы можем рассмотреть возможность включения его в пользовательскую структуру.
Добавьте следующую строку в начало файла:
use std::collections::HashMap
Это позволит нам напрямую использоватьHashMap
, без необходимости каждый раз вводить полный путь к пакету.
Под основной функцией добавим следующий код:
struct Todo {
// 使用 Rust 内置的 HashMap 来保存 key - val 键值对。
map: HashMap<String, bool>,
}
Это определит тип Todo, который нам нужен: структура с одним-единственным полем карты.
Это полеHashMapТипы. Вы можете думать об этом как о своего рода объекте JavaScript, который в Rust требует от нас объявления типов ключа и значения.
-
HashMap<String, bool>
Указывает, что у нас есть строковый ключ, значение которого является логическим: используется в приложении для представления активного состояния текущего элемента.
Как добавить методы в нашу структуру
Методы аналогичны обычным функциям — ониfn
ключевое слово для объявления, все принимают параметры и могут иметь возвращаемое значение.
Однако они отличаются от обычных функций тем, что определяются в контексте структуры, а их первый аргумент всегдаself
.
мы определимimplБлок кода (реализации) находится ниже недавно добавленной структуры выше.
impl Todo {
fn insert(&mut self, key: String) {
// 在我们的 map 中新增一个新的元素。
// 我们默认将其状态值设置为 true
self.map.insert(key, true);
}
}
Содержание функции простое: она использует встроенный HashMap.insertМетод вставляет переданный ключ в карту.
Два самых важных из них, которые нужно знать:
-
mut [doc]установить изменяемую переменную
- В Rust каждая переменная неизменяема по умолчанию. Если вы хотите изменить значение, которое вам нужно использовать
mut
ключевое слово, чтобы добавить вариативность к связанным переменным. Так как нашей функции нужно добавлять новые значения путем изменения карты, нам нужно сделать ее изменяемой.
- В Rust каждая переменная неизменяема по умолчанию. Если вы хотите изменить значение, которое вам нужно использовать
-
& [doc]Идентифицирует ссылку.
- Вы можете думать об этой переменной как об указателе на конкретное место в памяти, где хранится значение, а не как о непосредственном сохранении значения.
- В Rust это считаетсязанимать(заимствовать), что означает, что функция не владеет переменной, но указывает на место ее хранения.
Краткий обзор системы владения Rust
Вооружившись предыдущими знаниями о заимствованиях и ссылках, самое время кратко обсудить владение в Rust.
Владение — самая уникальная особенность Rust, она позволяет программистам Rust писать программы без ручного выделения памяти (например, в C/C++), при этом работая без сборщика мусора (например, JavaScript или Python). Rust постоянно просматривает содержимое программы. памяти для освобождения неиспользуемых ресурсов.
Система собственности имеет следующие три правила:
- У каждого значения в Rust есть переменная: его владелец.
- У каждого значения может быть только один владелец одновременно.
- Значение удаляется, когда владелец выходит за пределы области действия.
Rust проверяет эти правила во время компиляции, а это означает, что разработчик должен явно указать, нужно ли и когда освобождать значение в памяти.
Рассмотрим следующий пример:
fn main() {
// String 的所有者是 x
let x = String::from("Hello");
// 我们将值移动到此函数中
// 现在 doSomething 是 x 的所有者
// 一旦超出 doSomething 的范围
// Rust 将释放与 x 关联的内存。
doSomething(x);
// 由于我们尝试使用值 x,因此编译器将引发错误
// 因为我们已将其移至 doSomething 内
// 我们此时无法使用它,因为此时已经没有所有权
// 并且该值可能已经被删除了
println!("{}", x);
}
Эта концепция считается самой сложной для понимания при изучении Rust, поскольку она является новой для многих программистов.
Вы можете прочитать об этом в официальной документации Rust.право собственностидля более подробного объяснения.
Мы не будем углубляться в тонкости системы собственности. Теперь запомните правила, о которых я упоминал выше. Старайтесь на каждом этапе учитывать, нужно ли вам «владеть» значением и удалить его, или вам нужно продолжать ссылаться на него, чтобы его можно было сохранить.
Например, в приведенном выше методе вставки мы не хотим иметьmap
, потому что нам все еще нужно, чтобы он где-то хранил свои данные. Только тогда мы сможем окончательно освободить выделенную память.
Как сохранить карту на жесткий диск
Поскольку это демонстрационная программа, мы воспользуемся простейшим решением для долговременного хранения: запишем карту в файл на диск.
впусти насimpl
Создайте новый метод в блоке.
impl Todo {
// [其余代码]
fn save(self) -> Result<(), std::io::Error> {
let mut content = String::new();
for (k, v) in self.map {
let record = format!("{}\t{}\n", k, v);
content.push_str(&record)
}
std::fs::write("db.txt", content)
}
}
-
->
Указывает тип, возвращаемый функцией. То, что мы возвращаем здесь, являетсяResult
Типы. - Мы просматриваем карту и форматируем строку, содержащую как ключ, так и значение, разделенные табуляцией и заканчивающуюся символом новой строки в конце.
- Мы помещаем отформатированную строку в переменную содержимого.
- мы будем
content
контент с именемdb.txt
в файле.
Примечательно,save
имеет _собственность_ себя. В этот момент, если мы случайно попытаемся обновить карту после вызова save, компилятор заблокирует нас (потому что память self будет освобождена).
Это прекрасный пример того, как управление памятью в Rust можно использовать для создания более жесткого кода, который не будет компилироваться (чтобы предотвратить человеческий фактор во время разработки).
Как использовать структуры в main
Теперь, когда у нас есть эти два метода, мы можем начать их использовать. Теперь мы продолжим писать функциональность внутри основной функции, которую мы написали ранее: если предоставленное действие — это добавление, мы вставляем и сохраняем этот элемент в файл для будущего использования.
Добавьте следующий код под двумя привязками параметров, которые вы написали ранее:
fn main() {
// ...[参数绑定代码]
let mut todo = Todo {
map: HashMap::new(),
};
if action == "add" {
todo.insert(item);
match todo.save() {
Ok(_) => println!("todo saved"),
Err(why) => println!("An error occurred: {}", why),
}
}
}
Давайте посмотрим, что мы все сделали:
-
let mut todo = Todo
Давайте создадим структуру и привяжем ее к изменяемой переменной. - мы проходим
.
символ для вызоваTODO insert
метод. - мы будемсоответствоватьРезультат, возвращаемый функцией сохранения, и отображение сообщения на экране загрузки в разных случаях.
Давайте протестируем его. Откройте терминал и введите:
$ cargo run -- add "code rust"
todo saved
Проверим, действительно ли элемент сохранен:
$ cat db.txt
code rust true
вы можете найти это в этомgistНайдите полный фрагмент кода в формате .
Как прочитать файл
Теперь у нашей программы есть фундаментальный недостаток: каждый раз, когда «добавить» добавляет, мы переписываем всю карту вместо того, чтобы обновлять ее. Это потому, что мы создаем новый пустой объект карты каждый раз, когда программа запускается, давайте исправим это сейчас.
Добавлен новый метод в TODO
Мы реализуем новую функцию для структуры Todo. При вызове он прочитает содержимое файла и вернет сохраненное значение в нашу задачу. Обратите внимание, что это не метод, поскольку он не принимает self в качестве первого параметра.
мы называем егоnew
, что является просто соглашением Rust (см.HashMap::new()
).
Добавим в блок impl следующий код:
impl Todo {
fn new() -> Result<Todo, std::io::Error> {
let mut f = std::fs::OpenOptions::new()
.write(true)
.create(true)
.read(true)
.open("db.txt")?;
let mut content = String::new();
f.read_to_string(&mut content)?;
let map: HashMap<String, bool> = content
.lines()
.map(|line| line.splitn(2, '\t').collect::<Vec<&str>>())
.map(|v| (v[0], v[1]))
.map(|(k, v)| (String::from(k), bool::from_str(v).unwrap()))
.collect();
Ok(Todo { map })
}
// ...剩余的方法
}
Если приведенный выше код вызывает у вас головную боль, не волнуйтесь. Здесь мы используем более функциональный стиль программирования, в основном для того, чтобы показать парадигму, что Rust поддерживает многие другие языки, такие как итераторы, замыкания и лямбда-функции.
Давайте посмотрим, что именно происходит в приведенном выше коде:
- мы определяем
new
функция, которая возвращает тип Result, либоTodo
структура либоio:Error
. - Мы определяем различныеOpenOptionsнастроить способ открытия "db.txt". В частности
create(true)
Флаг, означающий создание файла, если он не существует. -
f.read_to_string(&mut content)?
Прочитайте все байты в файле и добавьте их кcontent
в строке.-
Уведомление: не забудьте добавить с помощью
std:io::Read
в верхней части файла и другие операторы использования для использованияread_to_string
метод.
-
Уведомление: не забудьте добавить с помощью
- Нам нужно преобразовать тип String в файле в HashMap. Для этого мы связываем переменную карты с этой строкой:
let map: HashMap<String, bool>
.- Это одна из тех ситуаций, когда у компилятора возникают проблемы с определением типа для нас, поэтому нам нужно объявить его самостоятельно.
- lines [документация]Создает итератор в каждой строке строки для перебора каждой записи в файле. потому что мы использовали в конце каждой записи
/n
формат. - map [документация]Принимает замыкание и вызывает его для каждого элемента итератора.
-
line.splitn(2, '\t')
[документация]Разделите каждую из наших строк на вкладки. -
collect::<Vec<&str>>()
[документация]— один из самых мощных методов в стандартной библиотеке: он преобразует итераторы в связанные коллекции.- Здесь мы сообщаем функции карты передать
::Vec<&str>
Venctor, прикрепленный к методу преобразования нашей строки Split в заимствованный фрагмент строки, на этот раз сообщает компилятору, какую коллекцию ожидать в конце операции.
- Здесь мы сообщаем функции карты передать
- Тогда для удобства воспользуемся
.map(|v| (v[0], v[1]))
Преобразуйте его в тип кортежа. - затем используйте
.map(|(k, v)| (String::from(k), bool::from_str(v).unwrap()))
Преобразуйте два элемента кортежа в String и boolean.- Примечание: не забудьте добавить
use std::str::FromStr;
в верхней части файла и другие операторы использования, чтобы иметь возможность использовать метод from_str.
- Примечание: не забудьте добавить
- В итоге мы собираем их в нашу HashMap. На этот раз нам не нужно объявлять тип, потому что Rust выводит его из объявления привязки.
- Наконец, если мы никогда не получим никаких ошибок, используйте
Ok(Todo { map })
Возвращает результат вызывающей стороне.- Обратите внимание, что, как и в JavaScript, если ключ и переменная имеют одно и то же имя в структуре, можно использовать более короткую запись.
phew!
Вы проделали хорошую работу! Изображение изrustacean.net/.
Другой эквивалентный способ
Хотя карта обычно считается более полезной, приведенное выше также может бытьfor
цикл для использования. Вы можете выбрать способ, который вам нравится.
fn new() -> Result<Todo, std::io::Error> {
// 打开 db 文件
let mut f = std::fs::OpenOptions::new()
.write(true)
.create(true)
.read(true)
.open("db.txt")?;
// 读取其内容到一个新的字符串中
let mut content = String::new();
f.read_to_string(&mut content)?;
// 分配一个新的空的 HashMap
let mut map = HashMap::new();
// 遍历文件中的每一行
for entries in content.lines() {
// 分割和绑定值
let mut values = entries.split('\t');
let key = values.next().expect("No Key");
let val = values.next().expect("No Value");
// 将其插入到 HashMap 中
map.insert(String::from(key), bool::from_str(val).unwrap());
}
// 返回 Ok
Ok(Todo { map })
}
Приведенный выше код функционально эквивалентен предыдущему функциональному коду.
Как использовать этот новый метод
В основном просто инициализируйте переменную todo следующим блоком кода:
let mut todo = Todo::new().expect("Initialisation of db failed");
Теперь, если мы вернемся к терминалу и выполним несколько команд «добавить», таких как следующие, мы должны увидеть, что наша база данных обновлена правильно.
$ cargo run -- add "make coffee"
todo saved
$ cargo run -- add "make pancakes"
todo saved
$ cat db.txt
make coffee true
make pancakes true
вы можете найти это в этомgistНайти весь полный код на текущем этапе в .
Как обновить значение в коллекции
Как и во всех приложениях todo, мы хотим иметь возможность не только добавлять элементы, но также иметь возможность выравнивать переключатели состояния и помечать их как выполненные.
Как добавить полный метод
Нам нужно добавить полный метод в структуру Todo. В нем мы получаем эталонное значение ключа и обновляем его значение. Если ключ не существует, вернутьNone
.
impl Todo {
// [其余的 TODO 方法]
fn complete(&mut self, key: &String) -> Option<()> {
match self.map.get_mut(key) {
Some(v) => Some(*v = false),
None => None,
}
}
}
Давайте посмотрим, что происходит с кодом выше:
- Объявляем возвращаемый тип метода: пустой
Option
. - весь метод возвращает
Match
результат выражения, которое будет пустымSome()
илиNone
. - Мы используем
*
[документация]оператор для разыменования значения и установки его в false.
Как использовать полный метод
Мы можем использовать «полный» метод, как мы это делали со вставкой.
существуетmain
функция, мы используемelse if
оператор, чтобы проверить, является ли действие, переданное в командной строке, «завершенным».
// 在 main 函数中
if action == "add" {
// add 操作的代码
} else if action == "complete" {
match todo.complete(&item) {
None => println!("'{}' is not present in the list", item),
Some(_) => match todo.save() {
Ok(_) => println!("todo saved"),
Err(why) => println!("An error occurred: {}", why),
},
}
}
Пришло время проанализировать, что мы сделали в приведенном выше коде:
- Если мы обнаружим, что возвращается значение Some, вызов todo.save навсегда сохранит изменения в нашем файле.
- мы совпадаем по
todo.complete(&item)
Параметр, возвращаемый методом. - Если результат возврата
None
, мы напечатаем предупреждение пользователю, чтобы обеспечить хороший интерактивный опыт.- мы проходим
&item
Передайте элемент как ссылку на метод «todo.complete», чтобы основная функция по-прежнему имела значение. Это означает, что мы можемprintln!
Макрос продолжает использовать эту переменную. - Если мы этого не сделаем, то значение будет использовано "complete" и в конечном итоге случайно отброшено.
- мы проходим
- Если мы обнаружим, что возврат
Some
стоимость, звонитеtodo.save
Навсегда сохраните это изменение в нашем файле.
Как и прежде, вы можетеgistНайдите весь соответствующий код на текущем этапе в .
запустить эту программу
Теперь пришло время полностью запустить разработанную нами программу в терминале. Давайте запустим программу с нуля, предварительно удалив предыдущий файл db.txt:
$ rm db.txt
Затем добавьте и измените операции в todos:
$ cargo run -- add "make coffee"
$ cargo run -- add "code rust"
$ cargo run -- complete "make coffee"
$ cat db.txt
make coffee false
code rust true
Это означает, что после выполнения этих команд у нас будет завершенный элемент («сварить кофе») и незавершенный элемент («код rust»).
Предположим, мы повторно добавляем кофейный элемент «приготовить кофе» в это время:
$ cargo run -- add "make coffee"
$ cat db.txt
make coffee true
code rust true
Дополнительно: как сохранить его в формате JSON с помощью Serde
Несмотря на то, что программа небольшая, она работает нормально. Кроме того, мы можем немного изменить логику. Для меня, пришедшего из мира JavaScript, я решил хранить значения в виде файлов JSON вместо обычных текстовых файлов.
Мы воспользуемся этой возможностью, чтобы узнать, как установить и использовать инструмент от сообщества открытого исходного кода Rust под названиемcreates.ioупаковка.
Как установить серде
Чтобы установить новые пакеты в наш проект, откройтеcargo.toml
документ. Внизу вы должны увидеть[dependencies]
Поля: Просто добавьте в файл следующее:
[dependencies]
serde_json = "1.0.60"
Достаточно. В следующий раз, когда мы запустим программу, Cargo скомпилирует нашу программу, загрузит и импортирует этот новый пакет в наш проект.
Как изменить Todo::New
В первую очередь мы собираемся использовать Serde при чтении файла базы данных. Теперь вместо файла «.txt» мы хотим прочитать файл JSON.
существуетimpl
В блоке кода мы больше похожиnew
метод:
// 在 Todo impl 代码块中
fn new() -> Result<Todo, std::io::Error> {
// 打开 db.json
let f = std::fs::OpenOptions::new()
.write(true)
.create(true)
.read(true)
.open("db.json")?;
// 序列化 json 为 HashMap
match serde_json::from_reader(f) {
Ok(map) => Ok(Todo { map }),
Err(e) if e.is_eof() => Ok(Todo {
map: HashMap::new(),
}),
Err(e) => panic!("An error occurred: {}", e),
}
}
Заметные изменения:
- опция файла больше не требуется
mut f
для привязки, потому что нам не нужно вручную назначать содержимое String, как раньше. Serde будет обрабатывать соответствующую логику. - Мы обновили расширение файла до
db.json
. -
serde_json::from_reader
[документация]десериализует файл для нас. Он мешает возвращаемому типу карты и пытается преобразовать JSON в совместимый HashMap. Если все пойдет хорошо, мы вернем структуру Todo как раньше. -
Err(e) if e.is_eof()
Являетсязащитник матча, что позволяет нам оптимизировать поведение оператора Match.- Если Serde возвращает преждевременный EOF (конец файла) как ошибку, это означает, что файл полностью пуст (например, при первом запуске или если мы удалили файл). В этом случае мы устраняем ошибку и возвращаем пустой HashMap.
- При всех других ошибках программа немедленно прерывается и закрывается.
Как изменить Todo.save
Еще одно место, где мы хотим использовать Serde, — это сохранение карты в формате JSON. Для этого поместите блок impl вsave
Метод обновлен до:
// 在 Todo impl 代码块中
fn save(self) -> Result<(), Box<dyn std::error::Error>> {
// 打开 db.json
let f = std::fs::OpenOptions::new()
.write(true)
.create(true)
.open("db.json")?;
// 通过 Serde 写入文件
serde_json::to_writer_pretty(f, &self.map)?;
Ok(())
}
Как и прежде, давайте посмотрим, какие изменения были сделаны здесь:
-
Box<dyn std::error::Error>
. На этот раз мы возвращаем код, содержащий общую реализацию ошибки Rust.Box.- Короче говоря, Box — это указатель на выделение в памяти.
- Поскольку открытие файла может вернуть ошибку Serde, мы на самом деле не знаем, какую из двух ошибок вернет функция.
- Поэтому нам нужно вернуть указатель на возможные ошибки, а не на сами ошибки, чтобы вызывающая сторона могла их обработать.
- Мы, конечно, изменили имя файла на
db.json
чтобы соответствовать имени файла. - Наконец, мы позволили Серде сделать тяжелую работу: записать HashMap в виде файла JSON.
- Не забудьте удалить из верхней части файла
use std::io::Read;
а такжеuse std::str::FromStr;
, потому что они нам больше не нужны.
Вот и все.
Теперь вы можете запустить свою программу и проверить, сохранен ли вывод в файл. Если все прошло хорошо, вы должны увидеть, что ваши задачи останутся в формате JSON.
вы можете найти это в этомgistЧитать полный код на текущем этапе в .
Эпилог, советы и другие ресурсы
Это был долгий путь, и для меня большая честь, что вы дочитали до этого места.
Я надеюсь, что вы узнали что-то новое из этого урока и вызвали больше любопытства. Не забывайте, что здесь мы говорим об очень "низкоуровневом" языке.
Это главная причина, по которой мне нравится Rust — Rust позволяет мне писать код, который является одновременно быстрым и эффективным с точки зрения памяти, не боясь брать на себя слишком большую ответственность за код: я знаю, что компилятор больше оптимизирует для меня, возможно, до запуска Early прерывание работы в случае ошибки.
Прежде чем я закончу, я хотел бы поделиться с вами некоторыми дополнительными советами и ресурсами, которые помогут вам продолжить ваше путешествие по Rust:
- Rust fmt — это очень удобный инструмент, который вы можете запускать по последовательному шаблону для форматирования кода. Больше не нужно тратить время на настройку вашего любимого плагина для линтера.
-
cargo check
[документация]Будет пытаться скомпилировать код, не запуская его: это становится полезным в тех случаях, когда вы просто хотите проверить правильность кода, не запуская его. - Rust поставляется с интегрированным набором тестов и инструментами для создания документации:cargo testа такжеcargo doc. Мы не будем вдаваться в них на этот раз, потому что в этом руководстве достаточно контента, возможно, в будущем.
Чтобы узнать больше о Rust, я думаю, что эти ресурсы действительно хороши:
- официальныйсайт ржавчины, место сбора всей важной информации.
- Если вам нравится общаться в чате, Discord RustсерверЭто очень активное и полезное сообщество.
- Если вы хотите учиться, читая, книга «Язык программирования Rust» — хороший выбор.
- Если вы предпочитаете видеоматериалы, Райан Левик "Введение в ржавчину«Видеосерия — отличный ресурс.
ты сможешьGithubНайдите соответствующий исходный код для этой статьи.
Иллюстрации в тексте взяты изrustacean.net/.
Спасибо за чтение и удачного кодирования!
Заключение переводчика
из-за"Искусство дено исследований«Предзнаменование языка Rust официально началось. При чтении этой статьи, если загрузка установочного пакета груза слишком медленная, вы можете установить источник груза наmirrors.ustc.edu.cn/.
Наконец, с заключением этого поста, январь подходит к концу. В феврале, помимо журналов Deno, на некоторое время будет приостановлена публикация статей. В течение этого периода основное внимание будет уделяться программированию и эффективному обучению, включая, помимо прочего:
- "Руководство по собеседованию ECMAScript+》: Создайте книгу интервью на 2021 год.
- "Тур по алгоритму Deno LeetCode》 - Предварительный перевод: используйте TypeScript для очистки алгоритма LeetCode под Deno.
- ...реализация большего количества моих вдохновений с открытым исходным кодом.
Добро пожаловать, чтобы продолжать обращать внимание! Гитхаб:github.com/hylerrix, официальный аккаунт (@ningowood).