当前位置: 首页>后端>正文

rustdesk客户端含中继服务器id rust服务器main

一、简介

Rust是一种预编译静态语言,这意味着你可以编译程序并将可执行文件发送给其他人。

二、安装

Windows直接去官网下载相关软件程序包即可。它可使用IDE软件进行开发。

三、编程

Rust并不关心代码存放位置。Rust源文件总是以 .rs扩展名结尾。Rust相关代码编写完成后需要先编译在运行
编写:Hello, world!

fn main() {
    println!("Hello, world!");
}

以在Windows上为例:

> rustc main.rs
> .\main.exe
Hello, world!

分析:Hello, world!

fn main() {

}

fn语法声明了一个信的函数,小括号()表明没有参数,大括号{}作为函数体的开始和结束。
main函数是一个特殊的函数:在可知性的Rust程序中,它总是最先运行的代码。第一个行代码声明了一个叫做main的函数,它没有参数也没有返回值。如果有参数,它们的名称应出现在小括号()中。
main函数中有如下代码:

println!("Hello, world!");
  1. Rust的缩进风格使用4个空格,而不是1个制表符(tab);
  2. println!调用了一个Rust宏(macro);如果是调用函数,则应输入println(没有`!)
  3. "Hello, world!"是一个字符串
  4. 该行以分号(;)结尾,这代表一个表达式的结束和下一个表达式的开始。大部分Rust代码行以分号结尾。

四、Cargo

Cargo是Rust的构建系统和包管理器。

1、创建Cargo项目

> cargo new hello_cargo
> cd hello_cargo

第一行新建了名为hello_cargo的目录和项目。而在这个目录下包含:一个Cargo.toml文件、一个src目录、以及位于src目录下的main.rs文件。
文件名:Cargo.toml

[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

这个文件使用TOML格式,这是Cargo配置文件的格式。
第一行,[package],是一个片段标题,表明下面的语句用来配置一个包。
之后的三行设置了Cargo编译程序所需的配置:项目名称、项目版本以及要使用的Rust版本。
最后一行,[dependencies]是罗列项目依赖的片段的开始,代码包被称为crates
Cargo将源文件放在src目录下。项目根目录只存放README、license信息、配置文件和其他跟代码无关的文件。

2. Cargo使用方式

  1. cargo new创建项目;
  2. cargo build构建项目;
  3. cargo run一步构建并运行项目;
  4. cargo check在不生成二进制文件的情况下构建项目并检查错误;

3. 发布(release)构建

当项目最终准备好发布时,可以使用cargo build --release来优化编译项目。这会在target/release而不是target/debug下生成可执行文件。

五、猜测数字游戏

use std::io;

fn main() {

    println!("Guess the number!");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()

        .read_line(&mut guess)

        .expect("Failed to read line");
  
    println!("You guessed: {}", guess);
}

默认情况下,Rust设定了若干会自动导入到每个程序作用域中的标准库内容,这组内容被成为预导入(preclude)内容。使用use预计显示将相关库引入作用域。

use std::io;

io库来自于标准库,也被称为std

fn main() {

main函数是程序的入口点。

1.使用变量存储值

let mut guess = String:new();

创建一个变量(variable)来存储用户输入。在Rust中,变量默认是不可变的。进而使用mut使得变量可变。
let mut guess会引入一个叫做guess的可变变量,等号=告诉Rust现在想将某值绑定在等号=右边的guess变量上。String是一个标准库提供的字符串类型,它是UTF-8编码的可增长文本块。
::new那一行的::语法表明newString类型的一个关联函数。关联韩式是针对类型实现的。一些语言中把它成为静态方法

2.接收用户输入

io::stdin()
    .read_line(&mut guess)

.read_line(&mut guess)调用read_line方法从便准输入句柄获取用户输入。将&mut guess作为参数传递给read_line()函数,将用户输入存储到这个字符串中。read_line()的工作是,无论用户在标准输入中键入什么内容,都将其追加(不会覆盖其原有内容)到一个字符传中,因此它需要字符串作为参数。这个参数应该是可变的,以便read_line()将用户输入附加上去。
&表示这个参数是一个引用(reference),它允许多处代码访问同一处数据,而无需在内存中多次拷贝。Rust的一个主要优势就是安全而简单的操纵引用引用默认情况是不可变的,因此需要写成&mut guess来使其可变。

3.使用Result类型来处理潜在错误

.expect("Failed to read line");

read_line会将用户输入附加到传递给它的字符串中,同时它也会返回一个类型为Result的值Result是一种枚举类型(enum)。枚举类型变量的值是多种可能状态中的一个,每种可能的状态称为一种枚举成员
Result的成员是OkErrOk表示成功,内部包含成功时产生的值;Err表示失败,包含失败的前因后果。
Result的实例拥有expect()方法。如果io::Result实例的值是Errexpect会导致程序崩溃,并显示当作参数给expect的信息。

4.使用println!占位符打印值

println!("You guessed: {guess}");
或
println!("You guessed: {}", guess);

第一个参数是格式化字符串,里面的{}是预留在特定位置的占位符。使用{}也可打印多个值:第一个{}使用格式化字符串之后的第一个值,第二个{}则使用第二个值。

六、生成一个秘密数字

Rust标准库尚未包含随机数功能,因此我们使用rand使得每次产生不通的秘密数字。

1.使用crate来增加更多功能

crate是一个Rust代码包。crate库可以包含任意能被其他使用的代码,但是不能自执行。
Cargo.toml导入rand

[package]
name = "guessing_game"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand = "0.8.3"

[dependencies]片段告诉Cargo本项目所依赖了哪些外部crate及其版本。0.8.3事实上是^0.8.3的简写,它表示任何至少是0.8.3但小于0.9.0的版本。

C:\Users\Xfsadmin\projects\guessing_game>cargo build
   Compiling cfg-if v1.0.0
   Compiling ppv-lite86 v0.2.17
   Compiling getrandom v0.2.8
   Compiling rand_core v0.6.4
   Compiling rand_chacha v0.3.1
   Compiling rand v0.8.5
   Compiling guessing_game v0.1.0 (C:\Users\Xfsadmin\projects\guessing_game)
    Finished dev [unoptimized + debuginfo] target(s) in 2.17s

现在我们有了一个外部以来,Cargo从registry上获取所有包的最新版本,这是一份来自Crates.io的数据拷贝。Crates.io是Rust生态环境中的开发者向他人贡献Rust开源项目的地方。

2.Cargo.lock文件确保构建是可重现的

Cargo有一个机制来确保任何人在任何时候重新构建代码,都会产生相同的结果:Cargo只会使用你指定的以来版本,除非你又手动制定了别的。

3.更新crate到一个新版本

当你确实需要升级crate时,Cargo提供了cargo update命令,它会忽略Cargo.lock文件,并计算出所有符合Cargo.toml声明的最新版本。Cargo接下来会把这些版本写入Cargo.lock文件。不过,Cargo默认智慧寻找大于0.8.3而小于0.9.0的版本(即默认升级不能跨越大版本)。

4.生成一个随机数

use std::io;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("The secret number is: {secret_number}");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}

Rng是一个trait,它定义了随机数生成器应实现的方法,想要使用这些方法的话,此trait必须在作用域中。

use rand::Rng;

这一行调用了rand::thread_rng函数提供实际使用的随机数生成器:它位于当前执行线程的本地环境中,并从操作系统获取seed。接着调用随机数生成器的gen_range方法。gen_range方法获取一个范围表达式作为参数,并生成一个在此范围之间的随机数。这类范围表达式使用了start..=end这样的形式,也就是说包含了上下断点,所需要执行1..=100来请求1和100之间的数。

let secret_number = rand::thread_rng().gen_range(1..=100);

七、比较猜测的数字和秘密数字

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("The secret number is: {secret_number}");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    println!("You guessed: {}", guess);

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }
}

增加了另一个use声明,从标准库引入了一个叫做std::cmp::Ordering的类型到作用域中。Ordering也是一个枚举,不过它的成员是LessGreaterEqual

match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }

新增的代码,它获取一个被比较值得引用:这里是把guesssecert_number作比较,然后它会返回一个刚才通过use引入作用域的Ordering枚举成员。使用match表达式,根据对guesssecret_number调用cmp返回的Ordering成员来决定接下来做什么。
一个match表达式由分支(arms)\构成。一个分支包含一个模式(pattern)\和表达式开头的值与分支模式相匹配时应该执行的代码。Rust获取提供给match的值并逐一检查每个分支的模式。

C:\Users\Xfsadmin\projects\guessing_game>cargo build
   Compiling guessing_game v0.1.0 (C:\Users\Xfsadmin\projects\guessing_game)
error[E0308]: mismatched types
   --> src\main.rs:22:21
    |
22  |     match guess.cmp(&secret_number) {
    |                 --- ^^^^^^^^^^^^^^ expected struct `String`, found integer
    |                 |
    |                 arguments to this function are incorrect
    |
    = note: expected reference `&String`
               found reference `&{integer}`
note: associated function defined here
   --> C:\Users\Xfsadmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\cmp.rs:789:8
    |
789 |     fn cmp(&self, other: &Self) -> Ordering;
    |        ^^^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `guessing_game` due to previous error

错误的核心表明这里由不匹配的类型(minsmatched types)。Rust有一个静态强类型系统,同时也有类型推断。当我们写出let guess = string::new()时,Rust推断出guess应该是String类型,并不需要我们指定类型;另一方面secret_number是数字类型,几个数字类型拥有1..=100之间的值:32位数字i32;32位无符号数字u32;64位数字i64/等等。Rust默认使用i32,所以它是secret_number的类型,除非增加类型信息,或任何能让Rust推断出不同类型的信息。因此错误的原因在于Rust不会比较字符串类型和数字类型。
所以我们必须把从输入中读取的String转换为一个真正的数字类型,才好与秘密数字进行比较。我们在main函数提供增加如下代码实现:

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("The secret number is: {secret_number}");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");
    
    let guess: u32 = guess.trim().parse().expect("Please type a number!");

    println!("You guessed: {}", guess);

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }
}

这里增加:

let guess: u32 = guess.trim().parse().expect("Please type a number!");

Rust允许用一个新值来隐藏(shadow)guess之前的值,这个功能常用在需要转换值类型之类的场景。它允许我们复用guess变量的名字,而不是被迫创建两个不同变量。
我们将这个新变量绑定到guess.trim().parse()表达式上。表达式中的guess指的是包含输入的字符串类型guess变量。String实例的trim方法会去除字符串开头和结尾的空白字符,我们必须执行此方法才能将字符串与u32比较,因为u32只能包含数值型数据。例如,当用户输入5并按下enter(在Windows上,按下enter会得到一个回车符和一个换行符,\r\n),guess看起来就会像:5\n5\r\n。因此trim方法会消除\n\r\n,只保留5
字符串的parse方法将字符串转换成其他类型。这里是把字符串转换为数值。parse方法只有在字符逻辑上可以转换为数字的时候才能工作,所以非常容易出错。parse方法也会返回Result类型。

八、使用循环来允许多次猜测

loop关键字创建了一个无限循环。

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("The secret number is: {secret_number}");
    
    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");
        
        let guess: u32 = guess.trim().parse().expect("Please type a number!");

        println!("You guessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => println!("You win!"),
        }
    }
}

九、猜测正确后退出

在代码中增加一行break,用户猜对了什么数字后会推出循环。

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("The secret number is: {secret_number}");

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");
        
        let guess: u32 = guess.trim().parse().expect("Please type a number!");

        println!("You guessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}

十、处理无效输入

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("The secret number is: {secret_number}");

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");
        
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        println!("You guessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}

expect调用换成match语句,从遇到错误就崩溃转换为处理错误。如果parse能将字符转换为一个数字,它会返回一个包含数字结果的Ok,这个Ok值与match第一个分支的模式相匹配,该分支对应的动作返回Ok值中的数字num,最后编程新创建的guess变量;如果不能将字符串转换为数字,它会返回一个包含更多错误信息的ErrErr不会匹配match第一个分支,但会匹配第二个分支Err(_)模式:_是一个通配符值,匹配所有。


https://www.xamrdz.com/backend/3gj1957515.html

相关文章: