由三个字符形成的字符串
OFF
是「Object File Format」的简写。在 Rust 语言里,字符串对应的类型是什么呢?
&str
若用 C 语言,可应用 char *
类型,例如
char *s = "OFF";
Rust 语言有相似的类型 &str
,例如
let s: &str = "OFF";
或省略 s
的类型申明
let s = "OFF";
rustc 认为双引号突围的符号串便是 str
类型的援用模式 &str
的字面量,因此可能推断出上例中 s
的类型。
C 语言可能通过指针或下标模式遍历字符串,例如
char *s = "OFF";for (char *p = s; *p != '\0'; p++) { printf("%c\n", *p);}size_t n = strlen(s);for (size_t i = 0; i < n; i++) { printf("%c\n", s[i]);}
Rust 语言可能通过下标模式遍历字符串,但过程有些波折,例如
let s = "OFF";let t = s.as_bytes();for i in 0 .. s.len() { let a: u8 = t[i]; let b: char = a as char; println!("{}", b);}
首先须要将字符串转化为字节数组,而后在遍历数组的过程中将字节数据转换为字符类型。假使应用字符串切片援用的办法,代码会优雅一些,例如
let s = "OFF";for i in 0 .. s.len() { println!("{}", &s[i .. i + 1]);}
不过,Rust 对字符串给出了简洁的语法糖,例如
let s = "OFF";for c in s.chars() { println!("{}", c);}
字符串实例办法 chars
返回的是迭代器。应用字符串的 chars
办法的益处是便于遍历 UTF-8 编码的字符串,例如
let s = "OFF 格局";for c in s.chars() { println!("{}", c);}
假使在遍历每个字符时心愿可能取得字符的下标,则可应用 char_indices
办法,例如
let hello = "你好,Rust!";for x in hello.char_indices() { let (a, b) = x; println!("{}, {}", a, b);}
或
let hello = "你好,Rust!";for x in hello.char_indices() { println!("{}, {}", x.1, x.2);}
输入后果为
0, 你3, 好6, ,9, R10, u11, s12, t13, !
char_indices
返回的迭代器产生的后果是元组(Turple)。上述代码展现了元组的根本用法。
字符串比拟
对于一个字符串,如何确定它的值是否为 "OFF"
呢?只需写一个可能比拟两个字符串是否相等的函数即可解决该问题。
fn str_eq(a: &str, b: &str) -> bool { let a_n = a.len(); let b_n = b.len(); if a_n != b_n { return false; } else { let a_bytes = a.as_bytes(); let b_bytes = b.as_bytes(); for i in 0 .. a_n { let a_i = a_bytes[i]; let b_i = b_bytes[i]; if a_i != b_i { return false; } } } return true;}
我的 str_eq
写得应该是有些俊俏,不过没关系,本意就是想证实我有如许不会 Rust。
上面测试 str_eq
是否正确工作:
let s = "OFF";println!("{}", str_eq(s, "OFF"));println!("{}", str_eq(s, "off"));println!("{}", str_eq(s, "O F F"));
输入
truefalsefalse
即便 str_eq
写得俊俏也没关系,在理论的代码里,我并不会应用它,因为 str
类型实现了一个叫作 Eq
的个性,可间接用 eq
函数进行字符串比拟。例如
let s = "OFF";println!("{}", s.eq("OFF"));
文本 -> 数字
有一个字符串,表白一个小数,例如
let s = "0.618";
如何将其中的数字解析为 f64
类型的值呢?
写一个将小数的字面值转化为小数的函数并不是很难,所以就作为无聊时打发工夫的练习题吧!在理论的我的项目里,通常能够应用 str
的实例办法 parse
办法解决该问题。例如
let a: f64 = "0.618".parse().unwrap();
str
的实例办法 parse
返回值的类型为 Result
,通常状况下须要基于模式匹配对其进行解构解决方能取得所需的值,例如:
let a: f64 = match "0.618".parse() { Ok(v) => v, Err(e) => panic!("Error: {}", e)};
上述针对 Result
类型的值的解构过程较为广泛,因而 Result
类型将上述过程定义为实例办法 unwrap
。
字符串集
有一个字符串,表白三个小数,以空格作为距离,例如
let s = "0.618 2.718 3.141";
如何将其宰割为三个字串,别离表白一个小数?
我晓得 str
类型有一个 split_whitespace
办法可能解决这个问题,然而当初为了近距离接触 Rust,我应该为此写一个简略的状态机,而不是用现成的办法。在写这个状态机之前,先确定如何表白字符串的宰割后果,亦即如何示意字符串集。无妨以 Vec<(usize, usize)>
类型示意字符串集,即以二元组为元素的向量,每个二元组用于记录待宰割字串中一段子字串的起止下标。
上面是一个试验,表明通过 Vec<(usize, usize)>
可能表白字符串的宰割后果。
let s = "0.618 2.718 3.141";let mut slices: Vec<(usize, usize)> = Vec::new();slices.push((0, 5));slices.push((6, 11));slices.push((12, 17));println!("({0}, {1}, {2})", &s[slices[0].0..slices[0].1], &s[slices[1].0..slices[1].1], &s[slices[2].0..slices[2].1]);
输入后果为
(0.618, 2.718, 3.141)
上述代码除蕴含了 Rust 元组的根本用法。,形如 &s[a..b]
的语法称为字符串切片,通过它可能拜访字符串中下标 a
到下标 b
之间的这段内容,若以数学区间的模式示意这段下标区间,可写为 [a, b),即前闭后开区间。
状态
要实现用于宰割字符串的状态机,还要思考如何表白状态。Rust 有枚举类型,可用于表白状态。例如
enum Status { Init, Space, NonSpace}
为什么有 Init
状态而没有 Stop
状态呢?因为字符串遍历过程本身可能终止,无需显式给出状态机的终止状态。
上面的代码可在遍历字符串的过程中依据字符设定状态:
fn display_status(m: &Status) { match m { Status::Init => println!("Init"), Status::Space => println!("Space"), Status::NonSpace => println!("NonSpace") }}fn main() { let s = "0.618 2.718 3.141"; let mut m = Status::Init; display_status(&m); for x in s.char_indices() { let (_, b) = x; if b == ' ' { m = Status::Space; } else { m = Status::NonSpace; } display_status(&m); }}
后果为
InitNonSpaceNonSpaceNonSpaceNonSpaceNonSpaceSpaceNonSpaceNonSpaceNonSpaceNonSpaceNonSpaceSpaceNonSpaceNonSpaceNonSpaceNonSpaceNonSpace
上述代码温习了变量所有权借用、条件表达式以及模式匹配等内容。不过,将条件表达式改为模式匹配,代码通常会简洁一些,例如
for x in s.char_indices() { match x.1 { ' ' => m = Status::Space, _ => m = Status::NonSpace } display_status(&m);}
状态机
状态机由一些在状态发生变化时触发的性能形成。上面这个状态机可能基于空格对字符串进行宰割:
let s = "0.618 2.718 3.141";let mut slices: Vec<(usize, usize)> = Vec::new();let mut m = Status::Init;for x in s.char_indices() { match m { Status::Init => { match x.1 { ' ' => m = Status::Space, _ => { m = Status::NonSpace; slices.push((x.0, x.0 + 1)); } } }, Status::Space => { match x.1 { ' ' => {}, _ => { m = Status::NonSpace; slices.push((x.0, x.0 + 1)); } } }, Status::NonSpace => { match x.1 { ' ' => m = Status::Space, _ => { slices.last_mut().unwrap().1 += 1; } } }, }}for slice in slices { println!("({}, {})", slice.0, slice.1);}
向量的 last_mut
办法能够返回 Result
类型,其中蕴含着向量最初一个元素的指针,且通过该指针能够批改该元素的值。假使仅仅是拜访向量的最初一个元素,可应用 last
办法。
小结
enum Status { Init, Space, NonSpace}fn split_str(s: &str) -> Vec<(usize, usize)> { let mut slices: Vec<(usize, usize)> = Vec::new(); let mut m = Status::Init; for x in s.char_indices() { match m { Status::Init => { match x.1 { ' ' => m = Status::Space, _ => { m = Status::NonSpace; slices.push((x.0, x.0 + 1)); } } }, Status::Space => { match x.1 { ' ' => {}, _ => { m = Status::NonSpace; slices.push((x.0, x.0 + 1)); } } }, Status::NonSpace => { match x.1 { ' ' => m = Status::Space, _ => { slices.last_mut().unwrap().1 += 1; } } }, } } return slices;}fn main() { let s = "0.618 2.718 3.141"; let slices = split_str(s); for slice in slices { println!("({}, {})", slice.0, slice.1); }}