BEGINNING RUST PROGRAMMING

Author: Ric Messier

如果需要电子书的小伙伴,可以留下邮箱,看到了会发送的

Chpater 1. Game of Life: The Basics

编写一个简单的游戏,首先是想象有一个二维平面,然后有很多格子,就像是Excel一样;然后,每一个格子里都装着,或者至少有可能装着一种生物,一种生活在单一细胞中的单细胞生物。这个游戏是进化的,意味着我们循环一代又一代,根据游戏规则决定每个细胞的生死

规则描述如下:

  1. 如果一个细胞当前是活的,但是它的邻居少于两个,它会因为缺乏支持而死亡。
  2. 如果一个细胞目前还活着,并且有两三个邻居,它将会存活到下一代。
  3. 如果一个细胞目前是活的并且有三个以上的邻居,它就会死于人口过剩(资源缺乏)。
  4. 如果一个细胞目前已经死亡,但有三个正好相邻的细胞,它就会复活。

然后是部分代码,接下来将会解释这段代码

extern crate rand;
use std::{thread, time};

fn census(_world: [[u8; 75]; 75]) -> u16
{
    let mut count = 0;
    for i in 0..74 {
        for j in 0..74 {
            if _world[i][j] == 1
            {
                count += 1;
            }
        }
    }
    count
}

fn generation(_world: [[u8; 75]; 75]) -> [[u8; 75]; 75]
{
    let mut newworld = [[0u8; 75]; 75];
    for i in 0..74 {
        for j in 0..74 {
            let mut count = 0;
            if i>0 {
                count = count + _world[i-1][j];
            }
            if i>0 && j>0 {
                count = count + _world[i-1][j-1];
            }
            if i>0 && j<74 {
                count = count + _world[i-1][j+1];
            }
            if i<74 && j>0 {
                count = count + _world[i+1][j-1]
            }
            if i<74 {
                count = count + _world[i+1][j];
            }
            if i<74 && j<74 {
                count = count + _world[i+1][j+1];
            }
            if j>0 {
                count = count + _world[i][j-1];
            }
            if j<74 {
                count = count + _world[i][j+1];
            }
            newworld[i][j] = 0;
            if (count <2) && (_world[i][j] == 1) {
                newworld[i][j] = 0;
            }
            if _world[i][j] == 1 && (count == 2 || count == 3) {
                newworld[i][j] = 1;
            }
            if (_world[i][j] == 0) && (count == 3) {
                newworld[i][j] = 1;
            }
        }
    }
    newworld
}

fn main() {
    let mut world = [[0u8; 75]; 75];
    let mut generations = 0;
    for i in 0..74 {
        for j in 0..74 {
            if rand::random() {
                world[i][j] = 1;
            } else {
                world[i][j] = 0;
            }
        }
    }
}

Bringing In External Functionality

extern crate rand;
use std::{thread, time};

Rust使用crates作为库去存储外部的一些可重用的函数。上面的两行代码就是代表引入了一些外部的函数来使用,它们不一样的是,第一行是引入的是外部的crates

extern这个关键字代表的是,编译器需要去其他地方或者说外部去查找这个库

在 2018 版次中,这个语句已经默认不需要了,因为Cargo会告诉编译器有哪些外部库,我们只需要使用use关键字引入

而当使用了这个关键字导入库,那就需要依赖Cargo这个工具确保库的位置,确保编译的成功,所以引入外部的库之前,需要在Cargo.toml这个文件中编写依赖关系

[package]
name = "lift"
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.7.2"

而第二行,则是引入标准库的函数,而且上面连续引入了两个模块

use std::thread;
use std::time;

其实是可以分开写的,但是也可以用一行将所有一样前缀的放在一起,用{}框起来

Namespaces

一个名称空间就只是一个容器而已,这是把一些相关的事物放到一起的方法,能够让它们保持一致性。

当编写程序的时候,我们会命名很多函数和变量,但是在其他的一些库或者模块中,也会有着一样的名称,我们需要有方法区分它们,让我们能够定位它们

Defining Variables

let mut world = [[0u8; 75]; 75];
let mut generations = 0;

使用let关键字定义变量,Rust默认是不可变的变量,就是说在定义之后,这个变量就不可以再变更了,像是一个常量一样

如果需要使用可变的变量,那就需要使用mut关键字

Datatypes

数字:i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64
字符:在Rust中,一个字符就是4个字节

Rust中,类型是后置的,不像Java,C#,它们是类型前置的,在声明变量中,我们可以显示指定类型

let yes_no: bool = true;

Arrays

在Rust中,数组不是一种数据类型,它是一种原始的数据结构

let list: [i32; 15];

数组的符号是[],里面第一个是表明数组内容的类型,分号后的表明数组的长度

也可以直接在[]里面填入初始值,用逗号分隔

let array: [i32; 15];
array = [3, 43, 12, 18, 90, 32, 8, 19];

也可以先声明变量,再初始化变量,但是上面的代码编译错误,原因是声明了15个元素,但是只填入了8个,所以编译错误。其实,数组不是类型,i32; 15是一个数据类型,所以是上下两个数据类型不匹配,所以导致的编译错误

Control Structures

for i in 0..74 {
    for j in 0..74 {
        if rand::random() {
            world[i][j] = 1;
        } else {
            world[i][j] = 0;
        }
    }
}

控制结构,一般就是循环和判定,Rust的循环看起来更加通俗易懂,上面的第一层循环就是i从0到74

LOOKING AT MORE FUNCTION FUNCTIONS

函数一般就是,可以返回一些值,因为可能需要函数内部会产生一些新的值并返回给调用者,调用者可能会使用返回值做一些判断;如果让函数更加有意义,函数需要数据,那就意味着我们需要传入一些数据给函数操作

Returning Values

fn census(_world: [[u8; 75]; 75]) -> u16
{
    let mut count = 0;
    for i in 0..74 {
        for j in 0..74 {
            if _world[i][j] == 1
            {
                count += 1;
            }
        }
    }
    count
}

在Rust中,一般不会使用return来返回结果,因为Rust是一个面向表达式的语言,它的所有块或者结构都是一个表达式,那么表达式是有返回值的,所以将结果放在函数的最后一行,也就代表函数的结果

在Rust中,也有语句,但是语句是没有返回值的,都是分号结尾的,我们可以发现方法的最后一行只摆放了count变量,但是没有分号,所以它是表达式,会返回值

除了一些常规的返回值,还可以返回tuple

let i: i32;
let b: bool;
(i, b) = function1();

Passing Parameters

简单地说,要将参数传递给函数,实际上需要在函数声明中声明参数。

在调用函数时,请记住调用参数被放置在堆栈上,以便被调用的函数可以访问它们。本地变量以及其他重要数据也在堆栈中。

我之所以在这里提到这个,是因为声明参数的原因之一是让编译器知道在调用函数时要在堆栈上为参数分配多少空间。

有些语言使用引用传递或值传递的思想。通过值传递意味着将值本身传递给函数。通过引用传递是指将数据的内存位置传递给函数

通过值传递基本上是只读的。只使用值,函数不能对数据进行任何更改,因此没有副作用。当函数完成并且执行被传递给调用函数时,传递到函数中的变量不会被触及。

通过引用传递允许被调用函数对数据进行更改,因为对存储数据的内存位置的直接访问被提供给被调用函数。这将允许被调用函数对该内存位置进行更改,以便当执行传递回调用函数时,已更改的值在调用函数中的该变量中可用。

Scope

Scope,简单地说,就是可以在其中引用变量并使其被理解的空间。

for i in 0..74 {
    println!("{}", i);
}

例如变量i,在{}这个块,i属于这个范围中,这意味着我们可以在这个块中使用变量i

假如尝试在这个块的外面去使用变量i,那么编译器就会报错,说在这个范围中没有找到变量i

范围规则并不总是直截了当的,尽管一旦你学会了它们,它们就很容易记住了。通常,可以说变量包含在由{}表示的代码块中。在函数中,定义在函数顶部的任何变量在函数结束时都将超出作用域。if后面的{}和for后面的{}都会创建一个作用域

在Rust中,会有一些额外的麻烦,因为这里的变量是有所有权的概念。

当我们用一个变量作为参数调用一个函数时,这个变量ーー更具体地说,这个变量所引用的数据所存储的内存位置就变成了被调用函数的属性。

原文地址:http://www.cnblogs.com/huangwenhao1024/p/16927113.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性