从一个 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还有很多特性帮助你编写更快更安全的特性

  • 开箱即用的依赖管理
  • 相当好的模块系统
  • 没有继承
  • 特征
  • 无畏的并发
  • 等等

 

posted @ 2021-09-17 16:15  Rogn  阅读(265)  评论(0编辑  收藏  举报