从零开始学习《Rust程序设计》Day1-Chapter1.1-2.5/导论及搭建web服务器

写在开头

我在学习Rust之前在嵌入式领域做了6年的C/C++开发,并轻量使用过Python。最近购入该书学习Rust,开帖记录学习过程遇到的问题及解决方案。
WechatIMG28

开发环境介绍

操作系统为MacOS,CPU为M2 pro,使用Clion 2025.1.7作为编辑器,使用rustc 1.91.1版本。
该书英文版为2021年出版,中文版于2023年出版。书中示例代码版本过旧,在实操中碰到了一些问题,我将会在帖子中记录解决方案。有的问题我并不清楚确切的原因,欢迎讨论。

问题记录

  1. Chapter2.5搭建web服务器问题记录

书中原版代码如下

Cargo.toml

[package]
name = "actix-gcd"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-web = "1.0.8"
serde = \{ version = "1.0", features = ["derive"]}

main.rs

use actix_web::{web, App, HttpServer, HttpResponse};
use serde::Deserialize; 

fn get_index() -> HttpResponse {
    HttpResponse::Ok()
        .content_type("text/html")
        .body(
            r#"
                <title>GCD Caculator</title>
                <form action="/gcd" method="post">
                <input type="text" name="n"/>
                <input type="text" name="m"/>
                <button type="submit">GCD Caculator</button>
                </form>
            "#,
        )
}

fn gcd(mut n:u64, mut m:u64) -> u64 {
    assert!(n != 0 && m != 0);
    while m != 0 {
        if m < n {
            let t = m;
            m = n;
            n = t;
        }
        m = m % n;
    }
    n
}

#[derive(Deserialize)]
struct GcdParams {
    n:u64,
    m:u64,
}

fn post_gcd(form: web::Form<GcdParams>) -> HttpResponse {
    if form.n == 0 || form.m == 0 {
        return HttpResponse::BadRequest()
            .content_type("text/html")
            .body("Computing the GCD with zero is boring.");
    }
    let response =
        format!("The greatest common divisor of the numbers {} and {} \
                is <b>{}<b>\n", form.n, form.m, gcd(form.n, form.m));
    HttpResponse::Ok()
        .content_type("text/html")
        .body(response)
}
fn main() {
    let server = HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(get_index))
            .route("/gcd", web::post().to(post_gcd))
    });
    println!("Serving on http://localhost:3000...");
    server
        .bind("127.0.0.1:3000").expect("Failed to bind server")
        .run().expect("error running server");
}

调试过程:

  1. 编译运行时报错
error[E0277]: cannot subtract `TimeDelta` from `Tm`
   --> /Users/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/actix-http-0.2.11/src/cookie/jar.rs:226:44
    |
226 |             cookie.set_expires(time::now() - Duration::days(365));
    |                                            ^ no implementation for `Tm - TimeDelta`
    |
    = help: the trait `Sub<TimeDelta>` is not implemented for `Tm`
    = help: the following other types implement trait `Sub<Rhs>`:
              `Tm` implements `Sub<time::Duration>`
              `Tm` implements `Sub`

解决方案
更新库版本至最新
Cargo.toml

[dependencies]
actix-web = "4.12.1"
serde = { version = "1.0.228", features = ["derive"]}
  1. 更新后产生新的错误
error[E0277]: the trait bound `fn() -> HttpResponse {get_index}: Handler<_>` is not satisfied
   --> src/main.rs:55:39
    |
 55 |             .route("/", web::get().to(get_index))
    |                                    -- ^^^^^^^^^ the trait `Handler<_>` is not implemented for fn item `fn() -> HttpResponse {get_index}`
    |                                    |
    |                                    required by a bound introduced by this call
    |
note: required by a bound in `Route::to`
   --> /Users/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/actix-web-4.12.1/src/route.rs:212:12
    |
210 |     pub fn to<F, Args>(mut self, handler: F) -> Self
    |            -- required by a bound in this associated function
211 |     where
212 |         F: Handler<Args>,
    |            ^^^^^^^^^^^^^ required by this bound in `Route::to`

该问题是由于新版actix_web不兼容老代码,需要做出以下修改:
函数需要用async修饰,例如fn get_index()==>async fn get_index()。修改get_indexpost_gcd即可。

  1. 修改后又出现新问题
error[E0599]: no method named `expect` found for struct `Server` in the current scope
  --> src/main.rs:61:16
   |
59 | /     server
60 | |         .bind("127.0.0.1:3000").expect("Failed to bind server")
   | |          ---------------------- method `expect` is available on `Result<HttpServer<{closure@src/main.rs:53:34: 53:36}, App<actix_web::app_service::AppEntry>, actix_web::app_service::AppInit<actix_web::app_service::AppEntry, BoxBody>, BoxBody>, std::io::Error>`
61 | |         .run().expect("error running server");
   | |               -^^^^^^ method not found in `Server`
   | |_______________|
   |

该问题同样是由于新版actix_web不兼容老代码,需要删除run后的expect:run().expect("error running server")==>run()

  1. 修改后出现warning
    warning: unused `Server` that must be used
    根据编译器提示,修改sever==>_ = sever

  2. 编译成功但服务器无法进入listen状态
    4.12.1版actix-web要求server run之后添加await使其进入listen状态
    修改代码.run()==>.run().await

  3. 编译再次报错
    提示
    error[E0728]: `await` is only allowed inside `async` functions and blocks
    根据提示,main函数也需要async修饰

  4. 提示main函数不允许添加async
    error[E0752]: `main` function is not allowed to be `async`.
    根据actix-web文档说明,需要在main函数前添加属性
    #[actix_web::main]

至此问题全部解决,可以在本地访问网页

附最终版代码

cargo.toml

[package]
name = "activ-gcd"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = "4.12.1"
serde = { version = "1.0.228", features = ["derive"]}

main.rs

use actix_web::{web, App, HttpServer, HttpResponse};
use serde::Deserialize;

async fn get_index() -> HttpResponse {
    HttpResponse::Ok()
        .content_type("text/html")
        .body(
            r#"
                <title>GCD Caculator</title>
                <form action="/gcd" method="post">
                <input type="text" name="n"/>
                <input type="text" name="m"/>
                <button type="submit">GCD Caculator</button>
                </form>
            "#,
        )
}

fn gcd(mut n:u64, mut m:u64) -> u64 {
    assert!(n != 0 && m != 0);
    while m != 0 {
        if m < n {
            let t = m;
            m = n;
            n = t;
        }
        m = m % n;
    }
    n
}

#[derive(Deserialize)]
struct GcdParams {
    n:u64,
    m:u64,
}

async fn post_gcd(form: web::Form<GcdParams>) -> HttpResponse {
    if form.n == 0 || form.m == 0 {
        return HttpResponse::BadRequest()
            .content_type("text/html")
            .body("Computing the GCD with zero is boring.");
    }
    let response =
        format!("The greatest common divisor of the numbers {} and {} \
                is <b>{}<b>\n", form.n, form.m, gcd(form.n, form.m));
    HttpResponse::Ok()
        .content_type("text/html")
        .body(response)
}

#[actix_web::main]
async fn main() {
    let server = HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(get_index))
            .route("/gcd", web::post().to(post_gcd))
    });
    println!("Serving on http://localhost:3000...");
    _ = server
        .bind("127.0.0.1:3000").expect("Failed to bind server")
        .run().await;
}
posted @ 2025-12-04 16:31  hust_qin  阅读(1)  评论(0)    收藏  举报