Разработка простого веб-приложения на Rust, часть 2a

сервер Программа перевода самородков Rust

Разработка простого веб-приложения на 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, что приводит к ошибке типа.

Я думаю, что три вещи трудно понять:

  1. На данный момент я не совсем уверен, как понять сообщение об ошибке. Что означают «ожидаемый» и «найденный»? Зная ответ, я понимаю, что «ожидаемый» относится кmainВозвращаемое значение, я могу четко понять «ожидаемое» и «найденное».
  2. Для меня см.Документацияне сразу понялtry!Как это влияет на возвращаемое значение функции, которая его вызывает. Конечно, я должен проверитьreturnОпределение в макросе. Однако, когда я нахожуДокументация по ржавчинев комментариях я понял, почему в этом примереtry!не может быть вmainпозвонил в.
  3. Эта ошибка на самом деле может проявляться в макросах. В то время я этого не понимал, но компилятор 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 Запись в файл

Давайте попробуем что-нибудь посложнее. как насчет этого:

  1. Попробуйте создать файл журнала. Создайте файл журнала, если он не существует.
  2. Попробуйте записать строку в файл журнала.
  3. Разберитесь во всем.

Первый пример не на полпути, продолжим:

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особенность. Но я действительно не знаю, как работают трейты или перечисления, и я думаю, что все это для меня излишне.

я проверюResultdocs, похоже, я иду в неправильном направлении: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 Обновление

  1. NMSpaz вRedditУказал на ошибку в моем примере.

Серия статей: Разработка простого веб-приложения на Rust

сноска:

1Ха-ха-ха-ха.


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из ИнтернетаНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллекти другие поля, если вы хотите видеть больше качественных переводов, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.