前言
本文阅读耗时1分钟
学习B站视频《Rust Web全栈开发教程》后做的笔记(WebApp
、WebAssembly
、WebService
),课程源码见Rust Web全栈开发源码
参考资料:
Rust的Web开发
WebAssembly
Rust and WebAssembly中文
Rust and WebAssembly
postgreSQL使用
目录
一、WebApp
二、WebAssembly
三、WebService
四、总结
部分知识点
derive
编译器可以通过#[derive]
为一些trait
提供基础的实现。 如果需要更复杂的逻辑,这些trait
也可以被手动实现
#[derive(Serialize, Debug, Clone, sqlx::FromRow)]
pub struct Course {
pub teacher_id: i32,
pub id: i32,
pub name: String,
pub time: Option<NaiveDateTime>,
pub description: Option<String>,
pub format: Option<String>,
pub structure: Option<String>,
pub duration: Option<String>,
pub price: Option<i32>,
pub language: Option<String>,
pub level: Option<String>,
}
它就实现了clone
接口和序列化成字符串接口。否则我们自己要去手动impl
这些trait
。相当于类型转换的规则
- 例如这些都编译器为基础类型实现了的
- 比较:Eq、PartialEq、Ord、PartialOrd
- Clone:从&T的一个拷贝创建T
- Copy:把一个类型的move转换为copy
- Hash:从&T计算它的哈希
- Default:创建一个数据类型的空实例
- Debug: 用{:?}格式化一个值
例如加了 sqlx::FromRow
这个,在我们读取数据库的时候直接转换映射成我们标记的数据模型类型
自定义值之间的转换
将 Json<UpdateCourse>
类型转换成 UpdateCourse
类型
impl From<web::Json<UpdateCourse>> for UpdateCourse{
fn from(course: web::Json<UpdateCourse>) -> Self{
UpdateCourse{
name: course.name.clone(),
description:course.description.clone(),
format: course.format.clone(),
structure: course.structure.clone(),
duration: course.duration.clone(),
price: course.price,
language: course.language.clone(),
level: course.level.clone(),
}
}
}
调用触发转换
update_course: web::Json<UpdateCourse>;
let a = update_course.into(); //a的类型就是UpdateCourse
或者可以利用try_from
,可以抛出转换时的异常
impl TryFrom<web::Json<CreateCourse>> for CreateCourse {
type Error = MyError;
fn try_from(course: web::Json<CreateCourse>) -> Result<Self, Self::Error> {
Ok(CreateCourse {
teacher_id: course.teacher_id,
name: course.name.clone(),
description: course.description.clone(),
format: course.format.clone(),
structure: course.structure.clone(),
duration: course.duration.clone(),
price: course.price,
language: course.language.clone(),
level: course.level.clone(),
})
}
}
触发调用
new_course: web::Json<CreateCourse>;
new_course.try_into()? ;//这样才是CreateCourse,如果有异常机会跑出去
代码中的?
let rows = sqlx::query!(r#"SELECT id, name, picture_url, profile FROM teacher"#)
.fetch_all(pool)
.await?;
这个rows
是 Vec<Record>
类型。但是如果不加?
呢,它就是Result<Vec<Record>>, Error
>类型。加了?
,如果这时候遇到错误了,会直接返回错误,抛到上层的。
格式化输出
我们想这样输出println!("{}",error)
,但是非基础类型没有实现Display
trait
,该怎么做呢?
impl fmt::Display for MyError{
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>{
write!(f, "{}", self)
}
}
包括其他的,例如Debug
trait
同理,自己实现一下,相当于类型转换的实现。
Web全栈开发中遇到的问题
-
数据库url连接失败导致编译失败
dotenv().ok(); let database_url = env::var("DATABASE_URL").expect("DATABASE_URL 没有在 .env文件里设置"); let db_pool = PgPoolOptions::new().connect(&database_url).await.unwrap();
在写代码的时候,Rust会主动去连接数据库,只要连接失败了,然后后面代码中利用
dp_pool
去查询等操作会报计算机拒绝访问等问题。 -
数据库表不存在或者字段错误导致编译失败
数据库中没有创建表,然后代码里又操作了这张表,会报红,也编译不了。或者字段错误也编译不了。这些都是编译前,在IDE
中自己会强制报红。
前提是数据库服务一定要先起来,否则你永远也编译不过代码。 -
属性不匹配导致编译失败问题
pub async fn get_teacher_details_db(pool: &PgPool, teacher_id: i32) -> Result<Teacher, MyError> { let row = sqlx::query!( "SELECT id, name, picture_url, profile FROM teacher where id = $1", teacher_id ) .fetch_one(pool) .await .map(|r| Teacher { id: r.id, name: r.name, //这里r是Record类型,但是是可以直接用的,具备语义化 picture_url: r.picture_url, //实际这些字段都报赋值失败 profile: r.profile, }) .map_err(|_err| MyError::NotFound("Teacher Id not found".into()))?; Ok(row) }
上述报错,String
不能直接使用Option<String>
赋值。
报错原因:因为数据库中teacher
表创建的时候,name
、picture_url
和profile
没有指定not null
,导致语义化后的类型是Option<String>
,包含了None
选择。
-
访问失败问题
//为啥不是 localhost:3000/courses pub fn course_routes(cfg: &mut web::ServiceConfig) { cfg.service(web::scope("/courses")) .route("/{teacher_id}/{course_id}", web::get().to(get_course_detail)); }
为啥不是 访问
localhost:3000/
能达到效果,因为限制域后的route
写错了,改成pub fn course_routes(cfg: &mut web::ServiceConfig) { cfg.service(web::scope("/courses") .route("/{teacher_id}/{course_id}", web::get().to(get_course_detail)) ); }
开发完后的感受
性能肯定是比Java
好的,Rust
的性能和体积优势很明显,Arctix
框架性能web
中榜首,单纯HelloWorld项目
而言,能达到1.6k
,没有深入研究对比,这里不作评价。网上也有很多做了比较,例如 Rust Web性能对比 。
但是作为新上手的开发来说,写起来成本会比较高,因为你在写代码的时候,好多问题都会在编译前提示你去解决,否则编译不过。到最后编译成功,可以说基本没有太多语法、内存上的BUG
了。
在写代码过程中,更加体会到了Rust
大多采用宏、函数式编程范式实现, 有点会让一些从JAVA
、C
之类语言入门的程序员感到不适应,Rust
没有继承,只有一套Trait
的定义,这个对应Java
的接口。
Rust WebAssembly
前端开发比较繁琐,抛开性能来说,还不如直接用js
。因为对于对于公司、团队来说,开发时间,简洁性、可维护性,这几个才有价值,而Rust
在前端的应用,学习成本和维护成本太高。但是对于后端而言,工具链完善,间接和可维护性我觉得属于还不错,前期开发时间可能会长一点,但是以后大概率会成为趋势。