- Оригинальный адрес:A Simple Web App in Rust, Part 2a
- Оригинальный автор:Joel's Journal
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:LeopPro
Разработка простого веб-приложения на Rust, часть 2a
1 Плюсы и минусы
Если вы не видели первую часть этой серии, пожалуйста, начнитездесьНачинать.
В первой части мы успешно создали проект на Rust и написали веб-приложение «Hello World».
Сначала в этой части я хотел написать программу, которая могла бы записывать дату в файловую систему. Но в процессе я долго мучился с проверкой типов, поэтому в этой части в основном пишет это.
2 начало
В прошлый раз было не так уж и плохо. Но когда я делал эту часть раньше, я помню, что это была самая трудная часть.
Давайте перейдем от существующегоmain.rs
Запустите, чтобы мы могли использовать новый файл.
$ pwd
/Users/joel/Projects/simple-log
$ cd src/
$ ls
main.rs
$ mv main.rs web_main.rs
$ touch main.rs
3 воспоминания о «Привет, мир»
Могу ли я написать «Hello World» самостоятельно без какой-либо ссылки?
Дай мне попробовать:
fn main() {
println!("Hello, world");
}
Потом:
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
Running `target/debug/simple-log`
Hello, world
О, кажется, я помню это. Я просто немного не уверен, нужно ли мнеprintln!
Импортировать что-то, что, кажется, лишнее.
4 Наивный подход
Хорошо, вперед. Поискав в Интернете «файл создания Rust», я нашелstd::fs::File
:doc.rust-wolf.org/bricks-and-mortar/happening/лосьон для тела…. Давайте попробуем пример:
use std::fs::File;
fn main() {
let mut f = try!(File::create("foo.txt"));
}
Скомпилировать:
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
<std macros>:5:8: 6:42 error: mismatched types:
expected `()`,
found `core::result::Result<_, _>`
(expected (),
found enum `core::result::Result`) [E0308]
<std macros>:5 return $ crate:: result:: Result:: Err (
<std macros>:6 $ crate:: convert:: From:: from ( err ) ) } } )
<std macros>:1:1: 6:48 note: in expansion of try!
src/main.rs:5:17: 5:46 note: expansion site
error: aborting due to previous error
Could not compile `simple-log`.
Когда я писал первую версию, мне действительно потребовалось много времени, чтобы исправить эту ошибку. Я больше не очень часто бываю в сообществе, поэтому решения этих проблем могут быть сырыми. Я был впечатлен выяснением этого, поэтому я сразу знал ответ.
Проблема с приведенным выше кодомtry!
Код расширения макроса возвращается в случае ошибкиErr
тип. тем не мениеmain
Возвращает тип устройства (()
)1, что приводит к ошибке типа.
Я думаю, что три вещи трудно понять:
- На данный момент я не совсем уверен, как понять сообщение об ошибке. Что означают «ожидаемый» и «найденный»? Зная ответ, я понимаю, что «ожидаемый» относится к
main
Возвращаемое значение, я могу четко понять «ожидаемое» и «найденное». - Для меня см.Документацияне сразу понял
try!
Как это влияет на возвращаемое значение функции, которая его вызывает. Конечно, я должен проверитьreturn
Определение в макросе. Однако, когда я нахожуДокументация по ржавчинев комментариях я понял, почему в этом примереtry!
не может быть вmain
позвонил в. - Эта ошибка на самом деле может проявляться в макросах. В то время я этого не понимал, но компилятор Rust может выводить исходный код, расширенный макросами. Эта функция упрощает отладку подобных проблем.
В третьем пункте мы упомянули макросы расширения. Просмотр макросов расширения — очень эффективный способ отладки такого рода проблем, и его стоит изучить.
5 Расширенные макросы отладки
Во-первых, я нашел эти коды, выполнив поиск «Макросы расширения Rust»:
use std::fs::File;
fn main() {
let mut f = try!(File::create("foo.txt"));
}
... мы можем сделать расширение выходного макроса компилятора следующим образом:
$ rustc src/main.rs --pretty=expanded -Z unstable-options
#![feature(no_std)]
#![no_std]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std as std;
use std::fs::File;
fn main() {
let mut f =
match File::create("foo.txt") {
::std::result::Result::Ok(val) => val,
::std::result::Result::Err(err) => {
return ::std::result::Result::Err(::std::convert::From::from(err))
}
};
}
На этот раз проще отлаживать. Макросы — очень эффективные инструменты, но, как и любой другой инструмент, нам нужно знать, когда и как их использовать.
Посмотрите, в выводе вышеreturn
Определение, проблема заключается здесь. он пытается вернутьErr
результат, ноmain
Функция ожидает тип модуля.
6 Борьба с типами
Я хочу знать, как решить эту проблему. я пытаюсь подражатьtry!
макрос, но на этот раз возвращает только тип ячейки:
use std::fs::File;
fn main() {
match File::create("foo.txt") {
Ok(val) => val,
Err(err) => ()
}
}
бегать:
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:5:5: 8:6 error: match arms have incompatible types:
expected `std::fs::File`,
found `()`
(expected struct `std::fs::File`,
found ()) [E0308]
src/main.rs:5 match File::create("foo.txt") {
src/main.rs:6 Ok(val) => val,
src/main.rs:7 Err(err) => ()
src/main.rs:8 }
src/main.rs:7:21: 7:23 note: match arm with an incompatible type
src/main.rs:7 Err(err) => ()
^~
error: aborting due to previous error
Could not compile `simple-log`.
Я не знаю, как выразить, "здесь ничего не надо делать". наверноеval
определенно естьstd::fs::File
тип, поэтому компилятор делает выводmatch
извсефилиалы должны вернуть его. я могу сделатьOk
Ветка тоже ничего не возвращает?
use std::fs::File;
fn main() {
match File::create("foo.txt") {
Ok(val) => (),
Err(err) => ()
}
}
бегать:
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:6:12: 6:15 warning: unused variable: `val`, #[warn(unused_variables)] on by default
src/main.rs:6 Ok(val) => (),
^~~
src/main.rs:7:13: 7:16 warning: unused variable: `err`, #[warn(unused_variables)] on by default
src/main.rs:7 Err(err) => ()
^~~
Running `target/debug/simple-log`
$ ls
Cargo.lock Cargo.toml foo.txt src target
он создалfoo.txt
! Конечно, код мог бы быть более элегантным, но пока он довольно хорош. Попробуем другой подход:
use std::fs::File;
fn main() {
File::create("foo.txt")
}
=>
$ rm foo.txt
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:5:5: 5:28 error: mismatched types:
expected `()`,
found `core::result::Result<std::fs::File, std::io::error::Error>`
(expected (),
found enum `core::result::Result`) [E0308]
src/main.rs:5 File::create("foo.txt")
^~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Could not compile `simple-log`.
To learn more, run the command again with --verbose.
Я видел это раньше. это означаетmain
функция возвращенаFile::create
результат. Я думаю, тут ничего возвращать не надо, но я не в этом направлении думаю. Что, если я добавлю точку с запятой?
use std::fs::File;
fn main() {
File::create("foo.txt");
}
=>
$ rm foo.txt
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:5:5: 5:29 warning: unused result which must be used, #[warn(unused_must_use)] on by default
src/main.rs:5 File::create("foo.txt");
^~~~~~~~~~~~~~~~~~~~~~~~
Running `target/debug/simple-log`
$ ls
Cargo.lock Cargo.toml foo.txt src target
Что ж, мы успешно запустились, и файл был создан, но теперь появляется предупреждение «неиспользованный результат». Давайте сделаем что-нибудь, чтобы обработать этот результат:
use std::fs::File;
fn main() {
match File::create("foo.txt") {
Ok(val) => println!("File created!"),
Err(err) => println!("Error: could not create file.")
}
}
=>
$ rm foo.txt
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:6:12: 6:15 warning: unused variable: `val`, #[warn(unused_variables)] on by default
src/main.rs:6 Ok(val) => println!("File created!"),
^~~
src/main.rs:7:13: 7:16 warning: unused variable: `err`, #[warn(unused_variables)] on by default
src/main.rs:7 Err(err) => println!("Error: could not create file.")
^~~
Running `target/debug/simple-log`
File created!
Теперь есть предупреждение о неиспользуемой переменной. Я предполагаю, что мы можем исправить это, используя многоточие или удаляя имена переменных:
use std::fs::File;
fn main() {
match File::create("foo.txt") {
Ok(..) => println!("File created!"),
Err(..) => println!("Error: could not create file.")
}
}
=>
cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
Running `target/debug/simple-log`
File created!
Видите, использование многоточия работает, а что произойдет, если я уберу многоточие?
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:6:12: 6:13 error: nullary enum variants are written with no trailing `( )`
src/main.rs:6 Ok() => println!("File created!"),
^
src/main.rs:7:13: 7:14 error: nullary enum variants are written with no trailing `( )`
src/main.rs:7 Err() => println!("Error: could not create file.")
^
error: aborting due to 2 previous errors
Could not compile `simple-log`.
Это не то, что я ожидал. Я предполагаю, что «нулевой» означает пустой кортеж, который необходимо удалить. Если убрать скобки:
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:6:9: 6:11 error: this pattern has 0 fields, but the corresponding variant has 1 field [
E0023]
src/main.rs:6 Ok => println!("File created!"),
^~
src/main.rs:7:9: 7:12 error: this pattern has 0 fields, but the corresponding variant has 1 field [
E0023]
src/main.rs:7 Err => println!("Error: could not create file.")
^~~
error: aborting due to 2 previous errors
Could not compile `simple-log`.
To learn more, run the command again with --verbose.
Это понятно и в принципе то, что я и предполагал. Мой разум формируется!
7 Запись в файл
Давайте попробуем что-нибудь посложнее. как насчет этого:
- Попробуйте создать файл журнала. Создайте файл журнала, если он не существует.
- Попробуйте записать строку в файл журнала.
- Разберитесь во всем.
Первый пример не на полпути, продолжим:
use std::fs::File;
fn log_something(filename, string) {
let mut f = try!(File::create(filename));
try!(f.write_all(string));
}
fn main() {
match log_something("log.txt", "ITS ALIVE!!!") {
Ok(..) => println!("File created!"),
Err(..) => println!("Error: could not create file.")
}
}
=>
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:3:26: 3:27 error: expected one of `:` or `@`, found `,`
src/main.rs:3 fn log_something(filename, string) {
^
Could not compile `simple-log`.
To learn more, run the command again with --verbose.
$
Поэтому я предполагаю, что параметры функции должны объявлять тип:
use std::fs::File;
fn log_something(filename: &'static str, string: &'static str) {
let mut f = try!(File::create(filename));
try!(f.write_all(string));
}
fn main() {
match log_something("log.txt", "ITS ALIVE!!!") {
Ok(..) => println!("File created!"),
Err(..) => println!("Error: could not create file.")
}
}
=>
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
<std macros>:5:8: 6:42 error: mismatched types:
expected `()`,
found `core::result::Result<_, _>`
(expected (),
found enum `core::result::Result`) [E0308]
<std macros>:5 return $ crate:: result:: Result:: Err (
<std macros>:6 $ crate:: convert:: From:: from ( err ) ) } } )
<std macros>:1:1: 6:48 note: in expansion of try!
src/main.rs:4:17: 4:45 note: expansion site
src/main.rs:5:12: 5:29 error: type `std::fs::File` does not implement any method in scope named `wr
ite_all`
src/main.rs:5 try!(f.write_all(string));
^~~~~~~~~~~~~~~~~
<std macros>:1:1: 6:48 note: in expansion of try!
src/main.rs:5:5: 5:31 note: expansion site
src/main.rs:5:12: 5:29 help: methods from traits can only be called if the trait is in scope; the f
ollowing trait is implemented but not in scope, perhaps add a `use` for it:
src/main.rs:5:12: 5:29 help: candidate #1: use `std::io::Write`
<std macros>:5:8: 6:42 error: mismatched types:
expected `()`,
found `core::result::Result<_, _>`
(expected (),
found enum `core::result::Result`) [E0308]
<std macros>:5 return $ crate:: result:: Result:: Err (
<std macros>:6 $ crate:: convert:: From:: from ( err ) ) } } )
<std macros>:1:1: 6:48 note: in expansion of try!
src/main.rs:5:5: 5:31 note: expansion site
src/main.rs:10:9: 10:15 error: mismatched types:
expected `()`,
found `core::result::Result<_, _>`
(expected (),
found enum `core::result::Result`) [E0308]
src/main.rs:10 Ok(..) => println!("File created!"),
^~~~~~
src/main.rs:11:9: 11:16 error: mismatched types:
expected `()`,
found `core::result::Result<_, _>`
(expected (),
found enum `core::result::Result`) [E0308]
src/main.rs:11 Err(..) => println!("Error: could not create file.")
^~~~~~~
error: aborting due to 5 previous errors
Could not compile `simple-log`.
To learn more, run the command again with --verbose.
Есть много ошибок. Давайте посмотрим на первую ошибку, я думаюlog_something
Функция должна указать возвращаемое значение. Я пробовал несколько вещей, но я застрял. Обратитесь к поисковикам!
Через несколько минут я наконец нашел ответ. Я нашел кое-что на GitHubСвязанная информация, но это не работает. Я попробовал около 50 различных решений и, наконец, заработал:
use std::io::prelude::*;
use std::fs::File;
fn log_something(filename: &'static str, string: &'static str) -> Result<File,std::io::error::Error> {
let mut f = try!(File::create(filename));
try!(f.write_all(string));
}
fn main() {
match log_something("log.txt", "ITS ALIVE!!!") {
Ok(..) => println!("File created!"),
Err(..) => println!("Error: could not create file.")
}
}
Я не знаю, почему это работает нормально. Если я правильно понимаю, тип возвращаемого значенияResult
Параметры должны бытьFile
иstd::io::error::Error
. Что именно это означает? Два типа для меня странные, один - фактический результат (файл), а другой -Error
тип. Зачем? Я думаю, что это нужно исправить снова после того, как я исправлю оставшиеся ошибки.
Теперь, когда я пытаюсь запустить его, я получаю следующее сообщение об ошибке:
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:8:22: 8:28 error: mismatched types:
expected `&[u8]`,
found `&'static str`
(expected slice,
found str) [E0308]
src/main.rs:8 try!(f.write_all(string));
^~~~~~
<std macros>:1:1: 6:48 note: in expansion of try!
src/main.rs:8:5: 8:31 note: expansion site
error: aborting due to previous error
Could not compile `simple-log`.
To learn more, run the command again with --verbose.
Я вижу в примере, что они добавляют строкуb
Хорошо, я игнорирую это, просто чтобы посмотреть, что произойдет. Параметры ремонта:
use std::io::prelude::*;
use std::fs::File;
fn log_something(filename: &'static str, string: &'static [u8; 12]) -> Result<File,std::io::error::Error> {
let mut f = try!(File::create(filename));
try!(f.write_all(string));
}
fn main() {
match log_something("log.txt", "ITS ALIVE!!!") {
Ok(..) => println!("File created!"),
Err(..) => println!("Error: could not create file.")
}
}
=>
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:4:85: 4:106 error: struct `Error` is private
src/main.rs:4 fn log_something(filename: &'static str, string: &'static [u8; 12]) -> Result<File, std::io::error::Error> {
^~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Could not compile `simple-log`.
Я знаю, что это пойдет не так. Потратьте некоторое время на поиск информации.
В документации по Rust естьглававводитьResult
. Похоже, я делаю что-то нестандартное. Я имею в виду, что это кажется «лучшим» способом справиться с текущей ошибкой, но я запутался. я видел этоunwrap
Несколько раз это выглядит так, как будто это то, что я хочу. если я попытаюсьunwrap
, все может быть иначе:
fn log_something(filename: &'static str, string: &'static [u8; 12]) {
let mut f = File::create(filename).unwrap();
f.write_all(string);
}
fn main() {
log_something("log.txt", b"ITS ALIVE!!!")
}
=>
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:6:5: 6:25 warning: unused result which must be used, #[warn(unused_must_use)] on by def
ault
src/main.rs:6 f.write_all(string);
^~~~~~~~~~~~~~~~~~~~
Running `target/debug/simple-log`
$ ls
Cargo.lock Cargo.toml foo.txt log.txt src target
$ cat log.txt
ITS ALIVE!!!
Видите, это сработало, несмотря на предостережение. Но я предполагаю, что это не путь Rust, Rust выступает за ранний сбой или выдачу ошибки.
настоящая проблема в томtry!
иtry!
макрос возвращает странныйResult
отделение:
return $crate::result::Result::Err($crate::convert::From::from(err))
Это означает, что все, что я передаю, должно реализовыватьFrom::from
особенность. Но я действительно не знаю, как работают трейты или перечисления, и я думаю, что все это для меня излишне.
я проверюResult
docs, похоже, я иду в неправильном направлении:doc.rust-lang.org/std/result/. здесьio::Result
Пример похож на то, что я сделал, поэтому позвольте мне посмотреть, смогу ли я решить проблему:
use std::io::prelude::*;
use std::fs::File;
use std::io;
fn log_something(filename: &'static str, string: &'static [u8; 12]) -> io::Result<()> {
let mut f = try!(File::create(filename));
try!(f.write_all(string));
}
fn main() {
match log_something("log.txt", b"ITS ALIVE!!!") {
Ok(..) => println!("File created!"),
Err(..) => println!("Error: could not create file.")
}
}
=>
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:5:1: 8:2 error: not all control paths return a value [E0269]
src/main.rs:5 fn log_something(filename: &'static str, string: &'static [u8; 12]) -> io::Result<()>
{
src/main.rs:6 let mut f = try!(File::create(filename));
src/main.rs:7 try!(f.write_all(string));
src/main.rs:8 }
error: aborting due to previous error
Could not compile `simple-log`.
To learn more, run the command again with --verbose.
Подумав некоторое время, я нашел проблему: должно быть вlog_something
последнее добавлениеOK(())
утверждение. я передаю ссылкуResult
Документация приходит к такому выводу.
Я привык к оператору без точки с запятой в конце значения функцииreturn ()
; и сообщение об ошибке «не все ветки возвращают один и тот же тип» непонятно — для меня это проблема несоответствия типов. Конечно,()
Вероятно, это не значение, которое я до сих пор сбиваю с толку.
Наш окончательный результат (этот пост):
use std::io::prelude::*;
use std::fs::File;
use std::io;
fn log_something(filename: &'static str, string: &'static [u8; 12]) -> io::Result<()> {
let mut f = try!(File::create(filename));
try!(f.write_all(string));
Ok(())
}
fn main() {
match log_something("log.txt", b"ITS ALIVE!!!") {
Ok(..) => println!("File created!"),
Err(..) => println!("Error: could not create file.")
}
}
=>
$ rm log.txt
$ cargo run
Running `target/debug/simple-log`
File created!
$ cat log.txt
ITS ALIVE!!!
Хорошо, получилось, вкуснятина! Я хочу закончить эту главу здесь, потому что содержание этой главы очень сложное. Я уверен, что этот код можно улучшить, но начнем,Следующая главаМы посмотрим на даты и время в Rust.
8 Обновление
- NMSpaz вRedditУказал на ошибку в моем примере.
—
Серия статей: Разработка простого веб-приложения на Rust
сноска:
1Ха-ха-ха-ха.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из ИнтернетаНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллекти другие поля, если вы хотите видеть больше качественных переводов, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.