从一个 PHP 开发者的角度来看 Rust
转自https://learnku.com/rust/t/29041
Rust不仅是一门底层系统编程语言,随着学习的深入,越发意识到它有成为一门高级编程语言和编写Web应用程序的潜力。使用Rust可以避免其他语言中容易出现的典型bug.
下面研究一下用Rust编写Web程序带来的好处。
强类型
在 Rust 中,所有内容都是强类型的,并在编译时进行检查。例如,如果程序中的函数接受 String 类型的参数,并且程序编译成功,则可以保证函数所接受的值只有字符串。因此,您可以避免编写在运行时检查参数的类型是否正确的防御性代码,或者参数是否为 null 的防御性代码。这使程序运行得更快,消除了与类型相关的错误的可能性,并使您编写的代码更少。
fn generate_greeting(name: String) -> String{ format!("hi, {}!", name) } fn main(){ let greeting = generate_greeting(String::from("Diego")); println!("{}", greeting); }
更进一步,它还强制表达式中使用的值的类型。例如 if 语句的条件只能使用布尔类型,其他语言中通常可以使用非布尔类型(整数、字符串)
以下代码将不会通过编译:
pub fn main() { let number = 0; if number { println!("number is not falsy!"); } }
编译器的提示信息很明确:
--> strong_type.rs:5:8 | 5 | if number { | ^^^^^^ expected `bool`, found integer
默认不可变
在我看来,在 Rust 中编写的另一个好处是,默认情况下,一切都是不可变的。这可以限制您在明确的情况下使某些需要改变的内容被允许改动,从而防止出现某些数据在您不打算改变时发生改变的意外情况。
fn walk_dog(dogs: &Vec<&str>){ for dog in dogs{ // dog = &"ww"; println!("Walking a {}", dog) } } fn main(){ let dog_collection = vec!["German Spitz", "Golden Retriever", "Akita"]; walk_dog(&dog_collection); assert_eq!(dog_collection, vec!["German Spitz", "Golden Retriever", "Akita"]); }
另一方面,我们可以放心地使用 Rust,它的方法不会改变数据,因为我们正在将一个不可变的引用传递给 dogs_collection 向量。如果 walk_dogs 方法试图以任何方式改变此数据,此代码将无法编译,因为它会破坏 Rust 的所有权规则。
但是,如果我们想让函数来改变数据,那么函数签名和调用函数的代码必须显式的传递和接受可变引用,如下例所示:
struct Product{ name: String, price: f32, stock: i32 } fn purchase_prodcut(product: &mut Product, qty: i32){ // 省略采购逻辑 product.stock -= qty; } fn main(){ let mut product = Product{name: String::from("Green"), price:50.5, stock:1000}; purchase_prodcut(&mut product, 200); println!("product.stock: {}", product.stock); assert_eq!(product.stock, 800); }
在这里你可以看到我们通过使用 mut 关键字就可以立即定义变量 product 为可变的,这告诉 Rust 我们打算改变这个变量,这引入了一些限制,例如我们一次只能对一个值进行可变引用,从而保护您的代码免受数据竞争冲突。 注意,purchase_product 函数签名还必须使用 &mut Product 语法明确指定它接受对 Product 的可变引用,编译器会确认这种情况。最后我们可以看到 product.stock 属性在函数内部发生了改变,订购的数量已被减去,产品 product 对象的库存 stock 现在为 800。
枚举和模式匹配
枚举和代数数据类型下Rust中应用广泛,用下面的例子来说明枚举和模式匹配的强大之处,我最喜欢的Rust设计是没有 null 类型。Rust变量没有空类型,那如何表示值缺失呢,你猜到了,那就是枚举。
pub enum Option<T> { None, Some(T), }
事实上,Rust用Option表示数据的缺失与存在,注意到Option还是一个泛型,表示没有值时Option是None,有值时是Some
下面通过一个例子看Option是怎么使用:
注意:例子中的Option是 std::option:Option,而不是我们自定义的Option
pub struct Customer { id: i32, name: String } pub fn get_customer_by_id(id: i32) -> Option<Customer> { //从数据库获取客户信息 } pub fn main() { let customer = get_customer_by_id(12); }
这里我们有一个 Customer 结构,把它想象成其他语言的类,比如 PHP 和从 DB 加载客户 customer 的函数。具有给定 id 的客户 customer 不存在于 DB 中的可能。因此,我们需要返回选项以允许不存在客户的可能性。
编译器不仅不允许你假设你有一个 Customer 的实例,它还会强制你完整处理函数返回的及枚举的每个可能的变量,从而使您的代码健壮。
// enum Option<T>{ // None, // Some(T), // } struct Customer{ id: i32, name: String } fn get_customer_by_id(id: i32) -> Option<Customer>{ // return Some(Customer{id: 12, name: "Rogn".to_string()}); } fn main(){ let customer = get_customer_by_id(12); match customer{ Some(a) => println!("Customer name: {}", a.name), None => println!("Customer not found"), } }
使用匹配运算符,我们能够处理调用 get_customer_by_id 函数的每个可能的结果。请注意,我们可以从枚举中提取数据,在这个例子中,Customer 实例最终匹配在匹配表达式的第一个臂柄的变量 a 中,然后我们可以在 => 的右侧使用它。
更多
这里我只是接触它的一点皮毛,Rust还有很多特性帮助你编写更快更安全的特性
- 开箱即用的依赖管理
- 相当好的模块系统
- 没有继承
- 特征
- 无畏的并发
- 等等