Rust通用编程概念

简介: Rust通用编程概念
rust在某些地方有比较新颖的特性,比如可变变量和不可变变量,shadowing隐藏变量等。本篇文章将带领大家快速学习Rust的一些通用编程概念。

1.变量与可变性

let变量

我们在声明变量时使用let关键字,默认情况下,变量是不可变的(immutable)。

let与const不同。let 与 const最大的区别在于初始化,let的初始化在程序中是不确定的,且声明与赋值可以分开。另外,const的初始化无类型推导,强迫开发者手动加类型注释。而且const后面必须是常量,初始化时的值是永远确定的。

如果我们要使变量为可变变量,需要在let后跟mut关键字。

fn main() {
    let x = 5;
    println!("The value of x is {}", x);

    // x = 6; //cannot assign twice to immutable variable `x`
    let mut y = 5;
    println!("The value of y is {}", y);
    y = 6;
    println!("The value of y is {}", y);
}

const常量

常量在绑定值以后也是不可变的的,但与不可变变量有区别:

  • 不可以使用mut,常量永远都是不可变的。
  • 声明常量使用const关键字,必须标注类型。
  • 常量可以在任何作用域内进行声明,包括全局作用域。
  • 常量只能绑定到常量表达式,无法绑定到函数的调用结果或只能在运行时才能计算数的值。

需要注意的是:在程序运行期间,常量在其声明的作用域内一直有效。

命名规范:Rust中常量使用全大写字母,每个单词之间使用下划线分开:MAX_POINTS

const MAX_POINTS: u32 = 100_000;//可在全局作用域中声明

fn main() {
    const MIN_POINTS: u32 = 10_000;//可使用下划线来分割,增强可读性
    println!("The value of MAX_POINTS is {}", MAX_POINTS);
    println!("The value of MIN_POINTS is {}", MIN_POINTS);
}

Shadowing(隐藏)

我们可以重复声明一个属性来对之前声明的变量进行隐藏,在后续的代码中的这个变量名代表的就是新的变量:

fn main() {
    let x = 5;
    let x = x + 1;
    let x = x * 2;
    println!("The value of x is {}", x) //The value of x is 12
}

shadow和把变量标记为mut是不一样的:

  • 如果不使用let关键字,那么重新给非mut的变量赋值将会导致编译时错误。
  • 而使用let声明的同名新变量,也是不可变的。
  • 使用let声明的同名新变量,它的类型可以与之前不同:
fn main() {
    let spaces = "    ";
    let spaces = spaces.len();
    println!("{}", spaces) //4
}

如果我们使用mut则会报错,因为类型不同:

fn main() {
    let mut spaces = "    ";
    spaces = spaces.len(); //expected `&str`, found `usize`
}

shadowing的设计得到的好处是非常明显的,比如我们先得到了一个字符串spaces_str,我们将其转换为数字spaces_num,但从表达的意图来说,这个后缀又是多余的,所以使用shadowing并不是坏处。

2.数据类型

Rust是静态编译语言,在编译时必须知道所有变量的类型。

  • 基于使用的值,编译器通常可以推断出它具体的类型。
  • 但如果可能的类型比较多(例如把String转为整数parse方法),就必须添加类型的标注,否则编译会报错:
fn main() {
    //如果不写u32类型,会报错:type annotations needed,因为编译器不知道该转为那种数字类型
    let guess: u32 = "42".parse().expect("Not a number");
    println!("{}", guess);
}

标量类型

一个标量类型代表一个单个的值

rust有4种主要的标量类型:

  • 整数类型
  • 浮点类型
  • 布尔类型
  • 字符类型

整数类型

整数类型没有小数部分,u32就是一个无符号的整数类型,占据32位的空间

  • 无符号整数类型以u开头
  • 有符号的整数类型以i开头

image-20221202194746413

有符号的类型有正有负,无符号的类型是非负数。

范围(设位数为n):

image-20221202195104165

isize和usize类型

由arch,计算机架构决定,如果是64位,就是64位的。

使用场景主要是对某种集合进行索引操作。

整数字面值

除byte类型外,所以数值类型字面值都允许使用类型后缀,如57u8。

不清楚用什么类型就用默认类型,整数是i32。

整数的溢出

u8范围为0-255,如果设为256:

  • 调试模式下编译会被检查出整数溢出,发生溢出,程序会panic(只会在程序运行时才回抛出来的异常)
  • --release下编译,不会检查,可能会导致溢出,如果溢出会发生“环绕”:256为0,257为1。但程序不会panic

浮点类型

f32,32位,单精度。f64(默认),64位,双精度。

数值操作

加减乘除余等

fn main() {
    let sum = 5 + 10; //i32
    let differnce = 95.5 - 4.3; //f64
    let product = 4 * 30; //i32
    let quotient = 56.7 / 32.2; //f64
    let reminder = 54 % 5; //i32
    let yu = 1.0 % 0.3; //f64 Rust支持对浮点类型的取余(两边都是同类型浮点数才行)
}

布尔

true/false,一个字节大写,类型为bool

字符

  • char类型被用来描述最基础的单个字符。
  • 字符类型的字面值使用单引号
  • 占用4字节大写
  • 是Unicode标量值

rust中的字符可能与一般理解的字符不同。

fn main() {
    let x = 'x'; //char
    let y: char = 'β'; //char
    let z = '😁'; //char
}

复合类型

  • 复合类型可以将多个值放在一个类型里。
  • rust提供了两种基础的复合类型:元组(Tuple)、数组

Tuple

可将多个类型的多个值放在一个类型里,Tuple的长度固定,一旦声明无法改变。

创建Tuple:在小括号中将值用逗号分开,每个位置都对应一个类型,Tuple中各元素的类型不必相同:

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    println!("{},{},{}", tup.0, tup.1, tup.2); //500,6.4,1
}
  • 获取tuple的元素值——使用模式匹配来解构
fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let (x, y, z) = tup;
    println!("{},{},{}", x, y, z); //500,6.4,1
}
  • 访问tuple的元素,使用点标记法:tup.0

数组

  • 数组也可以将多个值放在一个类型里。
  • 数组中每个元素的类型必须相同。
  • 数组的长度也固定
fn main() {
    let arr = [1, 2, 3]; //[i32; 3]
    println!("{},{},{}", arr[0], arr[1], arr[2]); //1,2,3
}
使用场景

如果想让数据存放在stack(栈)上而不是heap(堆)上,或者想保证有固定数量的元素,这时使用数组更有好处。

另外,数组没有Vector(标准库提供)灵活,并且Vector用得更多。

  • vector长度可变
  • 如果不确定用哪个,就用vector
数组类型
let arr:[i32; 3] = [1, 2, 3];
  • 如果数组的每个元素值都相同,那么可以在中括号中指定初始值,然后是;,最后是数组长度:
let arr = [5; 3];
println!("{},{},{}", arr[0], arr[1], arr[2]); //5,5,5
访问数组元素

使用索引arr[0]arr[1]

如果访问的索引超出了数组的范围,那么:

  • 编译会通过(不一定)
  • 运行会报错(panic),rust不允许其继续访问相应地址的内存。

3.函数

定义

  • 声明函数使用fn关键字
  • 针对函数和变量名,rust使用snake_case命名规范:所有字母都是小写,单词间使用下划线分开。
fn main() {
    println!("hello main");
    another_function();
}

fn another_function() {
    println!("Another func");
}

参数

parameters,arguments(形参,实参)

函数签名中必须指明所有参数的类型

fn main() {
    //arguments
    another_function(1, 2.2); //a=1,b=2.2
}

fn another_function(a: i32, b: f64) {//parameter
    println!("a={},b={}", a, b);
}

语句与表达式

函数的定义也是语句

语句不返回值,所以我们不能使用let将一个语句赋值给一个变量:

let x = (let y = 6);//error

let y = 6;//6是一个表达式,值为6,作为语句的一部分。

调用宏如println!("");也是一个表达式

下面的块表达式的值为4,如果在x+3后加;,则变成一个语句,返回一个空的tuple

fn main() {
    let x = 5;

    let y = {
        let x = 1;
        x + 3
    };

    println!("y={}", y); //y=4
}

函数的返回值

  • ->符号后边声明函数返回值的类型,但是不可以以返回值命名
  • rust中,返回值是函数体中最后表达式的值
  • 若想提前返回,需使用return,并指定一个值。
fn main() {
    let x = cc();
    println!("x={}", x); //x=5

    let y = dd(2);
    println!("y={}", y); //y=3
}

fn cc() -> i128 {
    5
}

fn dd(x: i128) -> i128 {
    x + 1
}

4.注释

单行//,多行/* *///

另外,rust中还有一种特殊的注释:“文档注释”

5.控制流

if表达式

  • 条件必须为bool类型,不像js这样会进行转换。
  • if表达式中,与条件相关联的代码块叫分支(arm)
  • 可选的,在后边可以加上一个else表达式
fn main() {
    let number = 3;
    if number < 5 {
        println!("true");
    } else {
        println!("false");
    }
}

else if

fn main() {
    let number = 6;
    if number % 4 == 0 {
        println!("divisible by 4");
    } else if number % 3 == 0 {
        println!("divisible by 3");
    } else if number % 2 == 0 {
        println!("divisible by 2");
    } else {
        println!("false");
    }
    //divisible by 3 按顺序执行
}

如果用了多于一个else if,最好使用match来重构代码

fn main() {
    judge(); //divisible by 2
}

fn judge() {
    let number = 6;
    for ele in [2, 4, 6] {
        let print = match number % ele {
            0 => ele.to_string(),
            _ => "false".to_string(),
        };
        return println!("divisible by {}", print);
    }
}

在let语句中使用if

因为if是一个表达式,所以可以将他放在let语句中等号的后面

fn main() {
    let condition = true;
    let numebr = if condition { 5 } else { 6 };
    println!("number={}", numebr); //number=5
}
//如果将6改为“6”,会报错。if和else的类型不兼容。无法在编译时确定number的类型,rust要求ifelse表达式中可能成为结果的分支返回的类型一致。

循环

loop,for,while

loop

反复循环,直到喊停。

可以使用break关键字。

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    println!("result is {}", result); //result is 20
}
while条件循环

每次执行循环体之前都判断一次条件:

fn main() {
    let mut counter = 3;

    while counter != 0 {
        println!("{}!", counter);
        counter = counter - 1;
    }
    println!("LIFTOFF!");
}
for循环遍历集合

可以使用while或loop遍历集合,但是易错且低效:

第一是index的范围容易搞错,二是低效,每次都会进行判断

fn main() {
    let a = [10, 20, 30, 40, 50];
    let mut index = 0;
    
    while index < 5 {
        println!("{}!", a[index]);

        index = index + 1;
    }
}

所以我们使用for循环来遍历:

fn main() {
    let a = [10, 20, 30, 40, 50];
    for ele in a.iter() {
        println!("{}!", ele);
    }
}

由于for的安全,简洁性,所以在rust中用得最多。

Range
  • 标准库提供
  • 指定一个开始数字和一个结束数字,range可以生成他们之间的数字(不包括结束)
  • rev方法可以反转Range
fn main() {
    for number in (1..4).rev() {
        println!("{}!", number);
    }
    println!("LIFTOFF");
}
// 3!
// 2!
// 1!
// LIFTOFF
相关文章
|
21天前
|
Rust 安全 Go
揭秘Rust语言:为何它能让你在编程江湖中,既安全驰骋又高效超车,颠覆你的编程世界观!
【8月更文挑战第31天】Rust 是一门新兴的系统级编程语言,以其卓越的安全性、高性能和强大的并发能力著称。它通过独特的所有权和借用检查机制解决了内存安全问题,使开发者既能享受 C/C++ 的性能,又能避免常见的内存错误。Rust 支持零成本抽象,确保高级抽象不牺牲性能,同时提供模块化和并发编程支持,适用于系统应用、嵌入式设备及网络服务等多种场景。从简单的 “Hello World” 程序到复杂的系统开发,Rust 正逐渐成为现代软件开发的热门选择。
37 1
|
15天前
|
Rust 网络协议 安全
Rust在系统编程中的案例分享
Rust在系统编程中的案例分享
31 10
|
21天前
|
Rust 开发者
揭秘Rust编程:模块与包的终极对决,谁将主宰代码组织的新秩序?
【8月更文挑战第31天】在软件工程中,模块化设计能显著提升代码的可读性、可维护性和可重用性。Rust 作为现代系统编程语言,其模块和包管理机制为开发者提供了强有力的工具来组织代码。本文通过对比模块和包的概念及使用场景,探讨了 Rust 中的最佳实践。
16 2
|
21天前
|
Rust 安全 JavaScript
探索Rust在系统编程领域的前景:虚拟机和编译器开发的新篇章
【8月更文挑战第31天】在系统编程领域,性能与安全性至关重要。Rust作为一种新兴语言,凭借其独特的内存安全和并发特性,正逐渐成为虚拟机和编译器开发的首选。本文通过案例分析,探讨Rust在这些领域的应用,例如Facebook的Compiler VM (CVM)项目和实验性的JavaScript JIT编译器Mithril。Rust的静态类型系统和所有权模型确保了高性能和安全性,而其强大的包管理和库生态则简化了虚拟机的开发。随着Rust社区的不断成熟,预计未来将有更多基于Rust的创新项目涌现,推动系统编程的发展。对于追求高性能和安全性的开发者而言,掌握Rust将成为一个重要战略方向。
38 1
|
21天前
|
Rust 安全 物联网
解锁物联网安全新纪元!Rust如何悄然革新系统级编程,让智能设备“零风险”连接未来?
【8月更文挑战第31天】随着物联网(IoT)技术的发展,设备安全与效率成为关键挑战。Rust语言凭借其内存安全、高性能和并发优势,逐渐成为物联网开发的新宠。本文通过智能门锁案例,展示Rust如何确保生物识别数据的安全传输,并高效处理多用户请求。Rust的应用不仅限于智能家居,还广泛用于工业自动化和智慧城市等领域,为物联网开发带来革命性变化。
32 1
|
20天前
|
开发者 vr&ar 机器学习/深度学习
Xamarin 开发者的未来趋势展望:掌握跨平台开发新机遇,引领移动应用创新潮流与技术变革方向
【8月更文挑战第31天】Xamarin 作为领先的跨平台开发框架,通过 C# 和 .NET 框架实现一次编写、多平台运行,简化了 iOS、Android 和 Windows 应用的开发流程。未来几年,Xamarin 开发者将面临跨平台开发普及、云集成、机器学习、AR/VR、性能优化及安全性等关键趋势。通过学习新技术并积极采用新工具,开发者能够提升应用质量和用户体验,如利用 Azure AD B2C 实现身份认证,从而在竞争激烈的市场中脱颖而出。
40 0
|
20天前
|
监控 UED 开发者
从设计到监控:用Xamarin打造高可用性移动应用的实战策略与案例深度剖析
【8月更文挑战第31天】在数字化时代,移动应用成为生活工作的重要组成部分,其稳定性和可靠性至关重要。Xamarin作为跨平台开发框架,已广受认可,但如何确保应用高可用性是开发者面临的挑战。本文以电商应用“SmartShop”为例,从设计、异常处理、性能优化、多线程及测试监控五个方面探讨构建高可用性Xamarin应用的最佳实践。通过模块化设计、有效异常处理、性能优化、多线程技术和全面测试监控,确保应用稳定高效,提升用户体验。
27 0
|
20天前
|
Rust 开发者 C#
解锁Rust高手的秘密武器:模式匹配与宏,学会这一招,编程效率翻倍!
【8月更文挑战第31天】Xamarin 是移动应用开发领域的强大跨平台工具,采用 C# 语言,具备高代码复用性、熟悉开发语言及接近原生性能等优势。开发者可通过共享项目实现多平台业务逻辑复用,简化开发流程。然而,Xamarin 也存在学习曲线陡峭、需处理平台差异及第三方库兼容性等问题。总体而言,Xamarin 在提高开发效率的同时,也对开发者提出了新的挑战。
15 0
|
21天前
|
Rust 安全 图形学
Rust图形革新:2D与3D编程的全新体验,它能否颠覆传统?
【8月更文挑战第31天】随着Rust语言的日益成熟,其在图形编程领域的应用逐渐增多。本文将探讨Rust在图形编程中的表现,从2D扩展至3D。通过使用`pixman`库处理2D图形,以及借助`naga`库实现3D渲染,展示了Rust在图形编程中的潜力。尽管与C++相比,Rust的生态仍在发展中,但其安全性与性能使其成为图形编程的重要工具之一,值得开发者关注和学习。
15 0
|
2月前
|
Rust 编译器 测试技术
Rust与C++的区别及使用问题之Rust中函数参数传递的问题如何解决
Rust与C++的区别及使用问题之Rust中函数参数传递的问题如何解决