Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Rust学习笔记

20
Jun
2019

Rust学习笔记

By Alex
/ in Rust
0 Comments
简介

Rust是Mozilla主导的编译型通用编程语言,它的特点包括:

  1. 内存高效,不包含运行时和垃圾回收器(对比Go语言)
  2. 性能接近于C++开发的应用程序
  3. 可靠性,丰富的内存系统、所有权模型保证内存安全、线程安全
起步
基于rustup

基于rustup是官方推荐的安装方式,执行下面的命令即可:

Shell
1
curl https://sh.rustup.rs -sSf | sh

你需要根据向导提示完成Rust编译器、Rust包管理器(Cargo)的安装。 默认情况下,所有工具被安装到~/.cargo/bin目录中。

Hello Rust

使用fn关键字可以声明一个函数,名为main的函数可以作为入口点:

Rust
1
2
3
4
fn main() {
    // 调用宏需要用!
    println!("Hello, Rust!");
}

调用下面的命令可以构建二进制文件:

Shell
1
rustc main.rs

默认的编译结果位于当前目录,和源文件名字一致,可以直接执行。 

Hello Cargo

Cargo是Rust的构建系统和包管理器,通过它可以创建Rust项目、构建项目、下载并编译依赖库。

创建项目

调用下面的命令创建新的Cargo项目:

Rust
1
cargo new hello_cargo

项目的布局如下:

Shell
1
2
3
4
5
6
7
8
├── Cargo.lock
├── Cargo.toml       # Cargo项目元数据文件
├── src              # 源码目录
│   └── main.rs
├── .gitignore       # 自动创建此文件
├── .git             # 自动创建Git仓库
└── target           # 存放构建结果
    └── debug
Cargo.toml

此文件格式为 TOML (Tom's Obvious, Minimal Language) ,声明Cargo项目的元数据、依赖:

TOML
1
2
3
4
5
6
7
[package]
name = "hello_cargo"
version = "0.1.0"
authors = ["Alex Wong <alex@gmem.cc>"]
edition = "2018"
 
[dependencies]

第二个段dependencies用于声明依赖,在Rust中代码包被称为crate,此项目没有依赖,因此dependencies段中没有crate。

构建
Shell
1
2
3
4
5
# 默认构建出调试版本:./target/debug/hello_cargo
cargo build
 
# 构建发布版本,耗时更多,代码更加优化
cargo build --release
运行

执行下面的命令构建并运行:

Shell
1
cargo run
检查

执行下面的命令可以快速的检查代码是否通过编译:

Shell
1
cargo check
语言
数据类型

Rust的数据类型分为两大类:标量(scalar)和复合(compound)。

标量类型包括:

标量类型 说明
整型

8-bit整数: i8(有符号) u8(无符号)
16-bit整数: i16 u16
32-bit整数: i32 u32
64-bit整数: i64 u64
其长度依赖于体系结构的整数:isize usize。在64bit机器上为64位

直接量语法:

Rust
1
2
3
4
5
6
7
8
9
10
11
12
13
# 除了byte类型(u8)之外,都可以加类型后缀
57u8
 
# 可以使用_作为分隔符,以方便阅读
1_000
 
# 不同进制表示
0xff         # 16进制
0o77         # 8进制
0b1111_0000  # 2进制
 
# Byte(u8)特殊表示法
b'A'

 关于整型溢出:u8可以存放的最大数为255,如果为其赋值256:

  1. debug编译模式下,导致Panic
  2. release编译模式下,不会进行溢出检测。256会变成0
浮点型 Rust支持两种精度的浮点数 f32 、 f64。
布尔型 bool, true或者 false
字符类型 char,使用单引号包围,代表了一个Unicode标量值,任何Unicode字符均可以表示

复合类型包括:

复合类型 说明
元组类型 将多个其它类型组合在一起:
Rust
1
let tup: (i32, f64, u8) = (500, 6.4, 1);

要获取元组的单个元素,可以进行解构(destructure)操作:

Rust
1
let (x, y, z) = tup;

或者,使用索引值来访问元素:

Rust
1
2
let five_hundred = x.0;
let six_point_four = x.1;
数组类型 类似于元组,但是每个元素的类型都必须相同:
Rust
1
let a = [1, 2, 3, 4, 5];

数组的类型名语法很特殊: [type; number],例如:

Rust
1
let a: [i32; 5] = [1, 2, 3, 4, 5];

数组元素的个数不可变化。访问数组元素的语法不同于元组:

Rust
1
2
let first = a[0];
let second = a[1];
关键字
use

默认情况下,Rust将 prelude模块中定义的类型自动引入到所有程序的作用域中。不再prelude的类型,可以使用use语句显式引入作用域:

Rust
1
2
3
4
5
use std::io;
io::stdin().read_line(&mut guess).expect("Failed to read line");
 
// 如果不用use,也可以使用全限定名称引用:
std::io::stdin().read_line(...);
let mut

此关键字用于定义一个可变变量 :

Rust
1
2
// 不需要声明变量类型
let mut guess = String::new();
let

此关键字用于定一个不可变变量。

Rust
1
let foo = bar;
.

可以用于基于索引来访问元组的元素:

Rust
1
let five_hundred = x.0; 

以及用来访问结构的方法、结构的字段。

:

可以用于显式的声明变量类型:

Rust
1
let f: bool = false;
::

此操作符用于访问类型的静态成员:

Rust
1
let mut guess = String::new();  // 关联函数,也叫静态方法

很多类型上都有名为new的关联函数,这种命名是Rust的一种惯用方式,用于创建类型的实例。

&

此关键字用于获得变量的引用:

Rust
1
io::stdin().read_line(&mut guess);

和变量类似, &mut guess表示可变引用, &guess则表示不可变引用。 

!

用于调用宏:

Rust
1
println!("You guessed: {}", guess);
[]

声明数组类型、数组直接量、访问数组元素时使用该关键字。

()

声明元组类型、数组直接量时使用该关键字。

声明函数形参、调用函数时,使用该关键字。

//

单行注释。

#

用于声明注解。

函数

关键字 fn用于定义一个新函数。在Rust中函数、变量名都使用snake case风格 —— 全部小写、使用下划线分隔单词。

函数声明示例:

Rust
1
2
3
fn another_function(x: i32) {
    println!("The value of x is: {}", x);
}

带有返回值的函数声明示例:

Rust
1
2
3
fn five() -> i32 {
    5
}

使用 return 关键字和指定值,可从函数中提前返回;但大部分函数隐式的返回最后的表达式 。

多值返回

要想返回多个值,可以使用元组:

Rust
1
2
3
4
fn calculate_length(s: String) -> (String, usize) {
    let length = s.len();
    (s, length)
} 
控制流
if

不需要括号,类似于Go语言:

Rust
1
2
3
4
5
6
7
if number % 4 == 0 {
    println!("number is divisible by 4");
} else if number % 3 == 0 {
    println!("number is divisible by 3");
} else {
    println!("number is not divisible by 4, 3, or 2");
}

if语句可以用在let语句中:

Rust
1
2
3
4
5
6
let condition = true;
let number = if condition {
    5
} else {
    6
};
loop 

重复执行直到break为止:

Rust
1
2
3
4
5
6
7
8
9
let mut counter = 0;
 
let result = loop {
    counter += 1;
 
    if counter == 10 {
        break counter * 2;
    }
};

可以看到,loop语句和if类似,可以用作let表达式的右值。 

while
Rust
1
2
3
4
5
6
7
let mut number = 3;
 
while number != 0 {
    println!("{}!", number);
 
    number = number - 1;
}
for

可以用来方便的迭代集合元素:

Rust
1
2
3
4
5
let a = [10, 20, 30, 40, 50];
 
for element in a.iter() {
    println!("the value is: {}", element);
}
所有权

所有权(ownership)系统是 Rust 最独特的功能,其令 Rust 无需垃圾回收(garbage collector)即可保障内存安全,同时不需要容易出错的手工内存管理。

何为所有权

对于每个Rust中的值,以下规则使用:

  1. Rust 中的每一个值都有一个被称为其 所有者(owner)的变量
  2. 值有且只有一个所有者
  3. 当所有者(变量)离开作用域,这个值将被丢弃

以String类型为例说明,和字符串直接量不同,它可以变化:

Rust
1
2
let mut s = String::from("hello");
s.push_str(", world!");   // 追加字符串

字符串直接量不可改变,在编译期长度可知, 因而可以在栈上分配,随着栈帧的销毁自动回收内存。

为了支持运行时动态改变,String类型是在堆上分配(编译时未知长度的内存空间),并在不需要String变量后将内存归还给OS。传统编程语言,要么手工释放内存,要么使用垃圾回收器,Rust使用了不同的方式处理内存释放:

Rust
1
2
3
4
5
{
    let s = String::from("hello"); // 从此处起,s 是有效的
    // 使用 s
}                                  // 此作用域已结束,
                                   // s 不再有效

当变量离开作用域后,Rust自动调用一个特殊的 drop函数,完成内存的释放。

变量的所有权总是遵循相同的模式:

  1. 将值赋给另一个变量时移动它
  2. 当持有堆中数据值的变量离开作用域时,其值将通过 drop 被清理掉,除非数据被移动为另一个变量所有
移动语义

考虑下面的语句:

Rust
1
2
3
4
let s1 = String::from("hello");
let s2 = s1;                     // 移动语义
 
println!("{}, world!", s1);      // Error

将s1“赋值”(移动)给s2后,Rust认为s1不再有效,因此第三个语句会报错:value used here after move。这和其它语言非常的不同。

由于s1已经无效,Rust就不会尝试调用 drop函数,也就不会遇到二次释放(double free)问题。

一个设计选择是:Rust 永远也不会自动创建数据的 “深拷贝”。因此,任何自动的复制可以被认为对运行时性能影响较小。

复制语义

Rust包含一个叫Copy trait的特殊注解,可以应用在类似整型这样的存储在栈上的类型上。如果一个类型拥有 Copy trait,一个旧的变量在将其赋值给其他变量后仍然可用——也就是说这种类型的变量的赋值操作具有复制语义,这相当于对上节的移动语义做了例外:

Rust
1
2
3
4
let x = 5;
let y = x;                          // 复制语义
 
println!("x = {}, y = {}", x, y);   // OK

Rust 不允许自身或其任何部分实现了 Drop trait 的类型使用 Copy trait。 

使用复制语义的类型包括:

  1. 整数
  2. 布尔
  3. 浮点数
  4. 字符
  5. 元组(所有元素均支持复制语义的话)
克隆操作

如果的确需要实现类似于C++/Go语言的赋值语义,可以使用通用的 clone函数:

Rust
1
2
3
4
let s1 = String::from("hello");
let s2 = s1.clone();
 
println!("s1 = {}, s2 = {}", s1, s2);
所有权和函数

将值传递给函数时的移动、赋值语义,和变量赋值时类似:

Rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
    let s = String::from("hello");  // s 进入作用域
 
    takes_ownership(s);             // 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 移出作用域。不会有特殊操作

可以注意到,在同一作用域内变量的销毁顺序,和它的声明顺序相反。 

和传参类似,返回值也牵涉到所有权的转移:

Rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fn main() {
    let s1 = gives_ownership();         // gives_ownership 将返回值
                                        // 移给 s1
 
    let 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 并移出给调用的函数
}
引用和借用

一个变量传递给函数后,在调用者处就无法使用,除非函数再次将其返回:

Rust
1
2
3
4
5
6
7
8
9
10
fn main() {
    let s1 = String::from("hello");
    let (s2, len) = calculate_length(s1);
    println!("The length of '{}' is {}.", s2, len);  // 调用函数后仍然需要使用s1指向的字符串
}
 
fn calculate_length(s: String) -> (String, usize) {
    let length = s.len();
    (s, length)  // 为了满足仍然需要使用的需求,需要将s的所有权再次转移回去
}

这种方式很罗嗦,因此Rust提供引用(references)功能来简化之:

Rust
1
2
3
4
//                     以String的引用为形参,不获得入参的所有权
fn calculate_length(s: &String) -> usize {
    s.len()
}

引用允许你使用值但不获取其所有权。 没有所有权,则离开作用域时就不会调用drop进行清理。

Rust中将获取引用作为函数参数称为 借用(borrowing)。 默认情况下,借用的变量是不支持修改的,除非使用mut关键字:

Rust
1
2
3
4
//                     可变引用
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

可变引用有一个很大的限制:在特定作用域中的特定数据有且只有一个可变引用。这些代码会失败:

Rust
1
2
3
4
let mut s = String::from("hello");
 
let r1 = &mut s;
let r2 = &mut s;   // 错误

同一作用域中,同时使用可变、不可变引用也不支持:

Rust
1
2
3
let r1 = &s;
let r2 = &s;       // 多个不可变引用是允许的
let r3 = &mut s;   // 混合使用可变、不可变引用不允许

再不同作用域中,使用两个可变引用则没有问题:

Rust
1
2
3
4
{
    let r1 = &mut s;
} // r1 在这里离开了作用域,所以我们完全可以创建一个新的引用
let r2 = &mut s;
悬垂引用

在具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针(dangling pointer)—— 其指向的内存可能已经被分配给其它持有者。 

在Rust中,编译期检查可以避免悬垂引用:

Rust
1
2
3
4
5
fn dangle() -> &String { // dangle 返回一个字符串的引用
    let s = String::from("hello"); // s 是一个新字符串
    &s // 返回字符串 s 的引用
} // 这里 s 离开作用域并被丢弃。其内存被释放
  // 危险!

这个例子中s是在函数体内创建,dangle具有它的所有权,返回&s仅仅是借用,而不获得s的所有权,这导致dangle函数结束时s被销毁,返回的是悬垂引用。编译器会识别这个错误。 

解决这个错误的方法很简单,那就是返回s而非它的引用,转移掉所有权:

Rust
1
2
3
4
fn no_dangle() -> String {
    let s = String::from("hello");
    s
}
切片

另一个没有所有权的数据类型是 slice。slice 允许你引用集合中一段连续的元素序列,而不用引用整个集合:

Rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let s = String::from("hello world");
 
// 切片语法,不包含结束索引
let hello = &s[0..5];
let world = &s[6..11];
 
// 包含结束索引
let hello = &s[0..=4];
let world = &s[6..=10];
 
// 从0开始
let slice = &s[..2];
// 直到结尾
let slice = &s[3..];
// 整个字符串
let slice = &s[..];

切片的本质是引用,对字符串的一部分的引用。 

结构

结构是一种自定义数据类型,和元组不同,结构的每个部分——字段——都具有名字:

Rust
1
2
3
4
5
6
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

创建结构实例的语法如下:

Rust
1
2
3
4
let mut alex = User {
    name: "Alex Wong".to_string(),
    age: 32,
};

要访问结构字段,可以使用点号:

Rust
1
alex.age = 33;
从既有实例创建
Rust
1
2
3
4
5
6
7
8
9
10
11
12
let mut alex = User {
    first_name: "Alex".to_string(),
    last_name: String::from("Wong"),
    age: 32,
};
 
let mut bo = User {
    first_name: "ChangBo".to_string(),
    age: 3,
    // 其它字段从alex这个实例复制
    ..alex
};
元组结构体 

元组结构体类似于元组,但是限定每个成员的类型,类似于结构,但是不指定成员的名称: 

Rust
1
2
3
4
5
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
 
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);

类似于元组,元组结构体支持解构操作、点号+索引访问成员。 

类单元结构体 

即unit-like structs,这是没有任何字段的结构体。它们类似于 ()即unit类型。当你需要为某个类型实现trait但却不需要在类型中存储任何数据时,可以使用类单元结构体。

所有权

结构体的字段,如果是非引用类型,则结构体本身对其拥有所有权。

如果字段是引用类型,则需要使用生命周期(lifetimes)这一特性,确保结构体引用数据的有效性和结构体本身一致。

方法

Rust支持为结构体添加行为,这种行为叫做方法,即位于结构体(以及下文讨论的枚举、Trait)的上下文中定义的函数。方法的第一个参数可以是:

  1. self,指向结构体实例
  2. &self,执行结构体实例的引用

方法语法如下:

Rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 这是一个注解,表示为下面的结构体保留调试信息
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
 
// 为结构体添加方法
impl Rectangle {
    // 第一个入参是结构的引用
    fn area(&self) -> u32 {
        self.width * self.height
    }
}
 
fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
 
    println!(
        "The area of the rectangle is {} square pixels.",
        // 使用点号语法访问结构方法
        rect1.area()
    );
}
自动引用和解引用

在调用方法时,Rust会自动进行获得引用、解引用操作,而不需要C++那样手工处理。

也就是说,当调用 object.something()时,Rust会按照需要添加 &、 &mut或者 *以匹配方法签名。

关联函数

除了方法,你还可以在 Impl块中定义不以self作为入参的函数,这种函数即关联(和结构相关,但是非实例方法)函数(associated functions),类似于C++中的静态函数。

在Rust中通常使用关联函数实现返回结构实例的新实例的工厂函数:

Rust
1
2
3
4
5
6
impl Rectangle {
    // 关联函数,第一个参数不是self
    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, height: size }
    }
}

调用关联函数时,需要使用结构体名::的语法形式: let sq = Rectangle::square(3);

枚举

定义枚举:

Rust
1
2
3
4
enum IpAddrKind {
    V4,
    V6,
}

创建枚举实例:

Rust
1
2
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

使用枚举,和普通类型一样:

Rust
1
2
3
4
5
6
7
8
9
10
11
fn route(ip_type: IpAddrKind) { }
 
struct IpAddr {
    kind: IpAddrKind,
    address: String,
}
 
let home = IpAddr {
    kind: IpAddrKind::V4,
    address: String::from("127.0.0.1"),
};
枚举字段

你可以将任何数据类型定义为枚举的字段,包括其它枚举。而且,每个枚举成员的字段可以各不相同:

Rust
1
2
3
4
5
6
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
枚举方法

和结构类似,枚举也可以有方法:

Rust
1
2
3
4
impl Message {
    fn call(&self) {
    }
}
Option

Rust没有引入其它语言支持的 null,null的缺陷是,你无法像使用普通值那样使用null值,这会导致错误。但是,“控制”所表示的概念的确是有意义的,为了解决此问题,Rust标准库提供了Option枚举:

Rust
1
2
3
4
enum Option<T> {
    Some(T),
    None,
}

由于Option的使用场景非常广泛,因此不需要显式导入即可使用Option及其成员: 

Rust
1
2
3
4
let some_number = Some(5);
let some_string = Some("a string");
// 使用None时,必须指定泛型参数,因为Rust不能自动推断
let absent_number: Option<i32> = None;

在对 Option<T>中的 T进行操作时,必须进行类型转换,这时有机会处理None值的情况。 

match控制流

类似于switch-case语句:

Rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => {
            println!("Lucky penny!");
            1
        },
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
        // 枚举成员的字段,可以在语句中访问
        Coin::Quarter(state) => {
            println!("State quarter from {:?}!", state);
            25
        },
    }
}

每个分支有两个部分:一个模式和一些代码,如果值匹配模式,则对应的代码就会执行。

Rust中的枚举必须穷尽所有case,无法列举则必须使用 _通配:

Rust
1
2
3
4
5
6
7
8
9
let some_u8_value = 0u8;
match some_u8_value {
    1 => println!("one"),
    3 => println!("three"),
    5 => println!("five"),
    7 => println!("seven"),
    // 类似于default语句
    _ => (),
}
if let控制流

可以用来简单的处理单个匹配,忽略其它匹配:

Rust
1
2
3
4
let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
    println!("three");
}

支持指定else分支:

Rust
1
2
3
4
5
if let Coin::Quarter(state) = coin {
    println!("State quarter from {:?}!", state);
} else {
    count += 1;
}
模块系统

和Rust模块系统相关的概念包括:

  1. 包(Package):是Cargo的功能,用于构建、测试、分享Crate
  2. Crate:一个模块的树状结构,提供库项目或者二进制项目
  3. 模块(Modules)+ use 关键字:用于控制作用域、路径的私有性
  4. 路径(path):命名结构、函数、模块等

 

标准库
基础类型
Result

Rust标准库的很多函数会返回Result类型:

Rust
1
2
let mut input = String::new();
let mut result = io::stdin().read_line(&mut input).expect("Error!");

std::result::Result是一个泛型枚举,它的两个成员是Ok、Error,表示操作是否成功。各子模块有Result的子类,例如上例中的 io::Result。

io::Result具有expect方法,如果Result为Error则调用expect会导致程序崩溃,传递给expect的消息会展示给用户。否则,该方法返回Result的值。

 

← Kubernetes的Service Catalog机制
在Kubernetes中管理和使用Jenkins →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

No related posts.

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2