如何制作一个多语言支持的PHP网站?
摘要:php多语言网站怎么做,杂谈发现一只网站是你们谁做的,西安网站建设hyk123,网络服务列表在哪里文档:Rust 程序设计语言 - Rust 程序设计语言 简体中文版 (bootcss.com) 墙裂推荐这个文
php多语言网站怎么做,杂谈发现一只网站是你们谁做的,西安网站建设hyk123,网络服务列表在哪里文档#xff1a;Rust 程序设计语言 - Rust 程序设计语言 简体中文版 (bootcss.com) 墙裂推荐这个文档 第一章入门
入门指南 - Rust 程序设计语言 简体中文版
第二章猜猜看游戏
猜猜看游戏教程 - Rust 程序设计语言 简体中文版 (bootcss.com)
// 导入库
use std::io;
use s…文档Rust 程序设计语言 - Rust 程序设计语言 简体中文版 (bootcss.com) 墙裂推荐这个文档 第一章入门
入门指南 - Rust 程序设计语言 简体中文版
第二章猜猜看游戏
猜猜看游戏教程 - Rust 程序设计语言 简体中文版 (bootcss.com)
// 导入库
use std::io;
use std::cmp::Ordering;
use rand::Rng;fn main() {println!(Guess the number!);// 生成一个随机数范围在[1, 100],需要在配置文toml中加依赖let secret_number rand::thread_rng().gen_range(1, 101);// loop循环loop {println!(Please input your guess.);// 定义一个可变变量加mut是可变的不加mut是不可变的let mut guess String::new();// 读一行数据到guess中返回值是Result(枚举成员是Ok和Err)// Result 拥有expect方法// 如果 io::Result 实例的值是 Errexpect 会导致程序崩溃并显示当做参数传递给 expect 的信息。// 如果 read_line 方法返回 Err则可能是来源于底层操作系统错误的结果。如果 io::Result 实例的值是 Okexpect 会获取 Ok 中的值并原样返回。io::stdin().read_line(mut guess).expect(Failed to read line);// Rust 允许用一个新值来 隐藏 shadow guess 之前的值// String 实例的 trim 方法会去除字符串开头和结尾的空白字符。// 字符串的 parse 方法 将字符串解析成数字。// parse也是有返回值成功的话就是返回num失败则继续输入let guess: u32 match guess.trim().parse() {Ok(num) num,Err(_) continue,};println!(You guessed: {}, guess);// 用match来匹配match guess.cmp(secret_number) {Ordering::Less println!(Too small!), // guess secret_numberOrdering::Greater println!(Too big!),Ordering::Equal {println!(You win!); // 匹配跳出循环break;}}}
}
代码量不大学了不少语法
测试 第三章常见编程概念
3.1 变量与可变性
变量不可改
fn main() {println!(Hello, world!);let i 3;println!(i {}, i);i 23; // 变量不可以更改println!(i {}, i);
}结果 可变变量可以修改
fn main() {println!(Hello, world!);let mut i 3;println!(i {}, i);i 23;println!(i {}, i);
}变量与常量的区别
1不允许对常量使用mut;
2声明常量使用 const 关键字而不是 let并且 必须 注明值的类型
3常量可以在任何作用域中声明
4常量只能被设置为常量表达式而不能是函数调用的结果或任何其他只能在运行时计算出的值。
const MAX_POINTS : u32 100_100;
隐藏Shadowing fn main() {let x 5;let x x 1;let x x * 2;println!(The value of x is: {}, x);
}
隐藏与将变量标记为 mut 是有区别的。当不小心尝试对变量重新赋值时如果没有使用 let 关键字就会导致编译时错误。通过使用 let我们可以用这个值进行一些计算不过计算完之后变量仍然是不变的。
mut 与隐藏的另一个区别是当再次使用 let 时实际上创建了一个新变量我们可以改变值的类型但复用这个名字。
3.2 数据类型
标量类型 标量scalar类型代表一个单独的值。Rust 有四种基本的标量类型整型、浮点型、布尔类型和字符类型。
整数
长度有符号无符号8-biti8u816-biti16u1632-biti32u3264-biti64u64128-biti128u128archisizeusize
整形字面值
数字字面值例子Decimal98_222Hex0xffOctal0o77Binary0b1111_0000Byte (u8 only)bA
浮点型
Rust 也有两个原生的 浮点数floating-point numbers类型它们是带小数点的数字。Rust 的浮点数类型是 f32 和 f64分别占 32 位和 64 位。默认类型是 f64因为在现代 CPU 中它与 f32 速度几乎一样不过精度更高。
Rust 中的所有数字类型都支持基本数学运算加法、减法、乘法、除法和取余。
fn main() {// 加法let _sum 5 10;// 减法let _difference 95.5 - 4.3;// 乘法let _product 4 * 30;// 除法let _quotient 56.7 / 32.2;// 取余let _remainder 43 % 5;
}
布尔型
正如其他大部分编程语言一样Rust 中的布尔类型有两个可能的值true 和 false。Rust 中的布尔类型使用 bool 表示。
字符类型
在 Rust 中拼音字母Accented letters中文、日文、韩文等字符emoji绘文字以及零长度的空白字符都是有效的 char 值。Unicode 标量值包含从 U0000 到 UD7FF 和 UE000 到 U10FFFF 在内的值。不过“字符” 并不是一个 Unicode 中的概念所以人直觉上的 “字符” 可能与 Rust 中的 char 并不符合。
fn main() {let c z;let z ℤ;let heart_eyed_cat ;
}
复合类型
复合类型Compound types可以将多个值组合成一个类型。Rust 有两个原生的复合类型元组tuple和数组array
元组是一个将多个其他类型的值组合进一个复合类型的主要方式。元组长度固定一旦声明其长度不会增大或缩小。
使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型而且这些不同值的类型也不必是相同的。
fn main() {let tup: (i32, f64, u8) (500, 6.4, 1);let tup (500, 6.4, 1);let (x, y, z) tup;println!(The value of y is: {}, y);let x: (i32, f64, u8) (500, 6.4, 1);let _five_hundred x.0; // 解构let _six_point_four x.1; // 解构let _one x.2; // 解构}
另一个包含多个值的方式是 数组array。与元组不同数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同因为 Rust 中的数组是固定长度的一旦声明它们的长度不能增长或缩小。
Rust 中数组中的值位于中括号内的逗号分隔的列表中。
fn main() {let a [1, 2, 3, 4, 5];let months [January, February, March, April, May, June, July,August, September, October, November, December];let a: [i32; 5] [1, 2, 3, 4, 5];let first a[0];let second a[1];let index 3;let element a[index];println!(The value of element is: {}, element);
}
3.3 函数如何工作
和C大同小异这里不同的是具有返回值的函数
fn five() - i32 {// 没有return// 没有分号5
}fn main() {let x five();println!(The value of x is: {}, x);
}3.4 注释
不赘述
3.5 控制流
if-else
fn main() {let number 6;// 注意没有括号if number % 4 0 {println!(number is divisible by 4);} else if number % 3 0 {println!(number is divisible by 3);} else if number % 2 0 {println!(number is divisible by 2);} else {println!(number is not divisible by 4, 3, or 2);}
}
在let语句中使用if
fn main() {let condition true;let number if condition {5} else {6};println!(The value of number is: {}, number);
}
if 的每个分支的可能的返回值都必须是相同类型
循环语句
Rust 有三种循环loop、while 和 for。
loop
fn main() {let mut counter 0;let result loop {counter 1;if counter 10 {break counter * 2;}};println!(The result is {}, result);
}
while
fn main() {let mut number 3;while number ! 0 {println!({}!, number);number number - 1;}println!(LIFTOFF!!!);
}
for
fn main() {let a [10, 20, 30, 40, 50];for element in a.iter() {println!(the value is: {}, element);}for number in (1..4).rev() {println!({}!, number);}println!(LIFTOFF!!!);
}
第四章认识所有权
所有权系统是 Rust 最为与众不同的特性它让 Rust 无需垃圾回收garbage collector即可保障内存安全。因此理解 Rust 中所有权如何工作是十分重要的。
4.1 所有权
所有运行的程序都必须管理其使用计算机内存的方式。一些语言中具有垃圾回收机制在程序运行时不断地寻找不再使用的内存在另一些语言中程序员必须亲自分配和释放内存。Rust 则选择了第三种方式通过所有权系统管理内存编译器在编译时会根据一系列的规则进行检查。在运行时所有权系统的任何功能都不会减慢程序。
所有权规则 Rust 中的每一个值都有一个被称为其 所有者owner的变量。值有且只有一个所有者。当所有者变量离开作用域这个值将被丢弃。 字符串字面值不可变这里以String为例
fn main() {println!(Hello, world!);// String在堆上// from 函数基于字符串字面值来创建 Stringlet mut str String::from(hello);str.push_str(, world); // push_str() 在字符串后追加字面值println!({}, str);
}结果 对于 String 类型为了支持一个可变可增长的文本片段需要在堆上分配一块在编译时未知大小的内存来存放内容。这意味着
必须在运行时向操作系统请求内存。需要一个当我们处理完 String 时将内存返回给操作系统的方法。
第一部分由我们完成当调用 String::from 时它的实现 (implementation) 请求其所需的内存。这在编程语言中是非常通用的。 然而第二部分实现起来就各有区别了。在有 垃圾回收garbage collectorGC的语言中 GC 记录并清除不再使用的内存而我们并不需要关心它。没有 GC 的话识别出不再使用的内存并调用代码显式释放就是我们的责任了跟请求内存的时候一样。从历史的角度上说正确处理内存回收曾经是一个困难的编程问题。如果忘记回收了会浪费内存。如果过早回收了将会出现无效变量。如果重复回收这也是个 bug。我们需要精确的为一个 allocate 配对一个 free。 Rust 采取了一个不同的策略内存在拥有它的变量离开作用域后就被自动释放。
fn main() {{let s String::from(hello); // 从此处起s 是有效的// 使用 s} // 此作用域已结束// s 不再有效
}注意在 C 中这种 item 在生命周期结束时释放资源的模式有时被称作 资源获取即初始化Resource Acquisition Is Initialization (RAII)。如果你使用过 RAII 模式的话应该对 Rust 的 drop 函数并不陌生。 变量与数据交互的方式一移动
fn main() {let x 5;let y x;
}基本变量赋值x和y都在栈中且值为5。
那么String类型呢
fn main() {let s1 String::from(hello);let s2 s1;
}String 由三部分组成如图左侧所示一个指向存放字符串内容内存的指针一个长度和一个容量。这一组数据存储在栈上。右侧则是堆上存放内容的内存部分。 长度表示 String 的内容当前使用了多少字节的内存。容量是 String 从操作系统总共获取了多少字节的内存。长度与容量的区别是很重要的不过在当前上下文中并不重要所以现在可以忽略容量。
当将 s1 赋值给 s2String 的数据被复制了这意味着我们从栈上拷贝了它的指针、长度和容量。我们并没有复制指针指向的堆上数据。 像C中的浅拷贝 如果 Rust 也拷贝了堆上的数据那么内存看起来就是这样的。如果 Rust 这么做了那么操作 s2 s1 在堆上数据比较大的时候会对运行时性能造成非常大的影响。 浅拷贝这就有了一个问题当 s2 和 s1 离开作用域他们都会尝试释放相同的内存。这是一个叫做 二次释放double free的错误也是之前提到过的内存安全性 bug 之一。两次释放相同内存会导致内存污染它可能会导致潜在的安全漏洞。
为了确保内存安全这种场景下 Rust 的处理有另一个细节值得注意。与其尝试拷贝被分配的内存Rust 则认为 s1 不再有效因此 Rust 不需要在 s1 离开作用域后清理任何东西。看看在 s2 被创建之后尝试使用 s1 会发生什么这段代码不能运行
fn main() {let s1 String::from(hello);let s2 s1;println!({}, world!, s1);}结果 如果你在其他语言中听说过术语 浅拷贝shallow copy和 深拷贝deep copy那么拷贝指针、长度和容量而不拷贝数据可能听起来像浅拷贝。不过因为 Rust 同时使第一个变量无效了这个操作被称为 移动move而不是浅拷贝。上面的例子可以解读为 s1 被 移动 到了 s2 中。那么具体发生了什么如图 这样就解决了之前的问题因为只有 s2 是有效的当其离开作用域它就释放自己的内存。
另外这里还隐含了一个设计选择Rust 永远也不会自动创建数据的 “深拷贝”。因此任何 自动 的复制可以被认为对运行时性能影响较小。
变量与数据交互的方式二克隆
如果 确实 需要深度复制 String 中堆上的数据而不仅仅是栈上的数据可以使用一个叫做 clone 的通用函数。
fn main() {let s1 String::from(hello);let s2 s1.clone();println!({}, world!, s1);println!({}, world!, s2);
}这段代码的实际结果就是如下图 只在栈上的数据拷贝
但这段代码似乎与我们刚刚学到的内容相矛盾没有调用 clone不过 x 依然有效且没有被移动到 y 中。
fn main() {let x 5;let y x;println!(x {}, y {}, x, y);
}原因是像整型这样的在编译时已知大小的类型被整个存储在栈上所以拷贝其实际的值是快速的。这意味着没有理由在创建变量 y 后使 x 无效。换句话说这里没有深浅拷贝的区别所以这里调用 clone 并不会与通常的浅拷贝有什么不同可以不用管它。
所有权与函数
将值传递给函数在语义上与给变量赋值相似。向函数传递值可能会移动或者复制就像赋值语句一样。
简单的案例见注释
fn main() {let s String::from(hello); // s 进入作用域takes_ownership(s); // s 的值移动到函数里 ...// ... 所以到这里不再有效// println!({}, s); // 报错let x 5; // x 进入作用域makes_copy(x); // x 应该移动函数里// 但 i32 是 Copy 的所以在后面可继续使用 x} // 这里, x 先移出了作用域然后是 s。但因为 s 的值已被移走// 所以不会有特殊操作fn takes_ownership(some_string: String) { // some_string 进入作用域println!({}, some_string);
} // 这里some_string 移出作用域并调用 drop 方法。占用的内存被释放fn makes_copy(some_integer: i32) { // some_integer 进入作用域println!({}, some_integer);
} // 这里some_integer 移出作用域。不会有特殊操作复杂一点的
fn main() {let s String::from(hello); // s 进入作用域takes_ownership(s); // s 的值移动到函数里 ...// ... 所以到这里不再有效// println!({}, s); // 报错
}
fn takes_ownership(some_string: String) { // some_string 进入作用域let str String::from(some_string);println!({}, some_string); // 报错
}返回值与作用域
返回值也可以转移所有权
见注释
fn main() {let s1 gives_ownership(); // gives_ownership 将返回值// 移给 s1let s2 String::from(hello); // s2 进入作用域let s3 takes_and_gives_back(s2); // s2 被移动到// takes_and_gives_back 中, // 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域但已被移走// 所以什么也不会发生。s1 移出作用域并被丢弃fn gives_ownership() - String { // gives_ownership 将返回值移动给// 调用它的函数let some_string String::from(hello); // some_string 进入作用域.some_string // 返回 some_string 并移出给调用的函数
}// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) - String { // a_string 进入作用域a_string // 返回 a_string 并移出给调用的函数
}变量的所有权总是遵循相同的模式将值赋给另一个变量时移动它。当持有堆中数据值的变量离开作用域时其值将通过 drop 被清理掉除非数据被移动为另一个变量所有。
在每一个函数中都获取所有权并接着返回所有权有些啰嗦。如果想要函数使用一个值但不获取所有权该怎么办呢如果还要接着使用它的话每次都传进去再返回来就有点烦人了除此之外也可能想返回函数体中产生的一些数据。可以使用元组来返回多个值
fn main() {let s1 String::from(hello);let (s2, len) calculate_length(s1);println!(The length of {} is {}., s2, len);
}fn calculate_length(s: String) - (String, usize) {let length s.len(); // len() 返回字符串的长度(s, length)
}好 返回参数的所有权
见引用部分
4.4 引用与借用
下面是如何定义并使用一个calculate_length 函数它以一个对象的引用作为参数而不是获取值的所有权
fn main() {let s1 String::from(hello);let len calculate_length(s1);println!(The length of {} is {}., s1, len);
}fn calculate_length(s: String) - usize {s.len()
}符号就是 引用它允许你使用值但不获取其所有权。 注意与使用 引用相反的操作是 解引用dereferencing它使用解引用运算符*。 变量 s 有效的作用域与函数参数的作用域一样不过当引用离开作用域后并不丢弃它指向的数据因为我们没有所有权。
将获取引用作为函数参数称为 借用borrowing。
正如变量默认是不可变的引用也一样默认不允许修改引用的值。
fn main() {let s String::from(hello);change(s);
}fn change(some_string: String) {some_string.push_str(, world);
}
结果 可变引用
fn main() {// s也必须是mut的let mut s String::from(hello);change(mut s);
}// 注意形参的形式
fn change(some_string: mut String) {some_string.push_str(, world);
}不过可变引用有一个很大的限制在特定作用域中的特定数据有且只有一个可变引用。
fn main() {let mut s String::from(hello);let r1 mut s;let r2 mut s; // 错误println!({}, {}, r1, r2);}
