跳到主要内容

基本数据类型

数字文字可以通过添加类型作为后缀来进行类型注释。例如,要指定文字 42 的类型应为 i32 ,请编写 42i32 。

无后缀数字文字的类型取决于它们的使用方式。如果不存在约束,编译器将使用 i32 表示整数,使用 f64 表示浮点数。

前面的代码中用到了一些概念还没有解释,这里给不耐烦的读者做一个简单的解释:

std::mem::size_of_val 是一个函数,但使用其完整路径进行调用。代码可以分割成称为模块的逻辑单元。在本例中, size_of_val 函数在 mem 模块中定义, mem 模块在 std 包中定义。有关更多详细信息,请参阅模块和 crate。

fn main() {
// Suffixed literals, their types are known at initialization
let x = 1u8;
let y = 2u32;
let z = 3f32;

// Unsuffixed literals, their types depend on how they are used
let i = 1;
let f = 1.0;

// `size_of_val` returns the size of a variable in bytes
println!("size of `x` in bytes: {}", std::mem::size_of_val(&x));
println!("size of `y` in bytes: {}", std::mem::size_of_val(&y));
println!("size of `z` in bytes: {}", std::mem::size_of_val(&z));
println!("size of `i` in bytes: {}", std::mem::size_of_val(&i));
println!("size of `f` in bytes: {}", std::mem::size_of_val(&f));
}

整型

Rust 标量类型都是通过自动拷贝的方式来赋值的。

整数int

fn main() {
let x:i32 = -32;
let y:u32 = 45;
let z:i64 = -3332;
let t:isize = 43;//大小与计算机架构相同
let r:usize = 143;//大小与计算机架构相同
//整形字面值
let a = 0xff;
let b = 98_22;
let c = 0b1111_0000;
let d = b'A';//表示char A的Unicode编码 输出用decimal表示
}

布尔bool

正如其他大部分编程语言一样,Rust 中的布尔类型有两个可能的值:truefalse。Rust 中的布尔类型使用 bool 表示。例如:

fn main() {
let t = true;

let f: bool = false; // 显式指定类型注解
}

浮点float

fn main() {
let x = 2.0; // f64 双精度

let y: f32 = 3.0; // f32 单精度
}

字符char

目前为止只使用到了数字,不过 Rust 也支持字母。Rust 的 char 类型是语言中最原生的字母类型,如下代码展示了如何使用它。(注意 char 由单引号指定,不同于字符串使用双引号。)

fn main() {
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻';
}

Rust 的 char 类型的大小为四个字节(four bytes),并代表了一个 Unicode 标量值(Unicode Scalar Value),这意味着它可以比 ASCII 表示更多内容。在 Rust 中,拼音字母(Accented letters),中文、日文、韩文等字符,emoji(绘文字)以及零长度的空白字符都是有效的 char 值。Unicode 标量值包含从 U+0000U+D7FFU+E000U+10FFFF 在内的值。不过,“字符” 并不是一个 Unicode 中的概念,所以人直觉上的 “字符” 可能与 Rust 中的 char 并不符合。

&T

比如&str,&[T]等

类型转换

Rust 提供了多种机制来更改或定义原始类型和用户定义类型。以下部分涵盖:

  • 原始类型之间的转换
  • 指定所需的文字类型
  • 使用类型推断
  • 别名类型

Rust 不提供原始类型之间的隐式类型转换(强制)。但是,可以使用 as 关键字执行显式类型转换(强制转换)。

整数类型之间的转换规则通常遵循 C 约定,除非 C 具有未定义的行为。 Rust 中明确定义了整型类型之间所有强制转换的行为。

// Suppress all warnings from casts which overflow.
#![allow(overflowing_literals)]

fn main() {
let decimal = 65.4321_f32;

// Error! No implicit conversion
let integer: u8 = decimal;
// FIXME ^ Comment out this line

// Explicit conversion
let integer = decimal as u8;
let character = integer as char;

// Error! There are limitations in conversion rules.
// A float cannot be directly converted to a char.
let character = decimal as char;
// FIXME ^ Comment out this line

println!("Casting: {} -> {} -> {}", decimal, integer, character);

// when casting any value to an unsigned type, T,
// T::MAX + 1 is added or subtracted until the value
// fits into the new type

// 1000 already fits in a u16
println!("1000 as a u16 is: {}", 1000 as u16);

// 1000 - 256 - 256 - 256 = 232
// Under the hood, the first 8 least significant bits (LSB) are kept,
// while the rest towards the most significant bit (MSB) get truncated.
println!("1000 as a u8 is : {}", 1000 as u8);
// -1 + 256 = 255
println!(" -1 as a u8 is : {}", (-1i8) as u8);

// For positive numbers, this is the same as the modulus
println!("1000 mod 256 is : {}", 1000 % 256);

// When casting to a signed type, the (bitwise) result is the same as
// first casting to the corresponding unsigned type. If the most significant
// bit of that value is 1, then the value is negative.

// Unless it already fits, of course.
println!(" 128 as a i16 is: {}", 128 as i16);

// In boundary case 128 value in 8-bit two's complement representation is -128
println!(" 128 as a i8 is : {}", 128 as i8);

// repeating the example above
// 1000 as u8 -> 232
println!("1000 as a u8 is : {}", 1000 as u8);
// and the value of 232 in 8-bit two's complement representation is -24
println!(" 232 as a i8 is : {}", 232 as i8);

// Since Rust 1.45, the `as` keyword performs a *saturating cast*
// when casting from float to int. If the floating point value exceeds
// the upper bound or is less than the lower bound, the returned value
// will be equal to the bound crossed.

// 300.0 as u8 is 255
println!(" 300.0 as u8 is : {}", 300.0_f32 as u8);
// -100.0 as u8 is 0
println!("-100.0 as u8 is : {}", -100.0_f32 as u8);
// nan as u8 is 0
println!(" nan as u8 is : {}", f32::NAN as u8);

// This behavior incurs a small runtime cost and can be avoided
// with unsafe methods, however the results might overflow and
// return **unsound values**. Use these methods wisely:
unsafe {
// 300.0 as u8 is 44
println!(" 300.0 as u8 is : {}", 300.0_f32.to_int_unchecked::<u8>());
// -100.0 as u8 is 156
println!("-100.0 as u8 is : {}", (-100.0_f32).to_int_unchecked::<u8>());
// nan as u8 is 0
println!(" nan as u8 is : {}", f32::NAN.to_int_unchecked::<u8>());
}
}

类型推断

类型推断引擎非常智能。它不仅仅在初始化期间查看值表达式的类型。它还研究了之后如何使用变量来推断其类型。这是类型推断的高级示例:

fn main() {
// Because of the annotation, the compiler knows that `elem` has type u8.
let elem = 5u8;

// Create an empty vector (a growable array).
let mut vec = Vec::new();
// At this point the compiler doesn't know the exact type of `vec`, it
// just knows that it's a vector of something (`Vec<_>`).

// Insert `elem` in the vector.
vec.push(elem);
// Aha! Now the compiler knows that `vec` is a vector of `u8`s (`Vec<u8>`)
// TODO ^ Try commenting out the `vec.push(elem)` line

println!("{:?}", vec);
}

不需要变量的类型注释

aliasing

type 语句可用于为现有类型指定新名称。类型必须具有 UpperCamelCase 名称,否则编译器将发出警告。此规则的例外是原始类型: usize 、 f32 等。

// `NanoSecond`, `Inch`, and `U64` are new names for `u64`.
type NanoSecond = u64;
type Inch = u64;
type U64 = u64;

fn main() {
// `NanoSecond` = `Inch` = `U64` = `u64`.
let nanoseconds: NanoSecond = 5 as u64;
let inches: Inch = 2 as U64;

// Note that type aliases *don't* provide any extra type safety, because
// aliases are *not* new types
println!("{} nanoseconds + {} inches = {} unit?",
nanoseconds,
inches,
nanoseconds + inches);
}

别名的主要用途是减少样板代码;例如 io::Result<T> 类型是 Result<T, io::Error> 类型的别名。

Rust 通过使用特征来解决自定义类型(即 struct 和 enum )之间的转换。通用转换将使用 From 和 Into 特征。然而,对于更常见的情况,有更具体的方法,特别是在 String 之间进行转换时。

类型转换

原始类型可以通过强制转换相互转换。

Rust 通过使用特征来解决自定义类型(即 struct 和 enum )之间的转换。通用转换将使用 From 和 Into 特征。然而,对于更常见的情况,有更具体的方法,特别是在 String 之间进行转换时。

From

From 特征允许类型定义如何从另一种类型创建自身,从而提供了一种非常简单的机制来在多种类型之间进行转换。标准库中有许多此特征的实现,用于原始类型和常见类型的转换。

例如我们可以轻松地将 str 转换为 String

let my_str = "hello";
let my_string = String::from(my_str);

我们可以做类似的事情来定义我们自己的类型的转换。

use std::convert::From;

#[derive(Debug)]
struct Number {
value: i32,
}

impl From<i32> for Number {
fn from(item: i32) -> Self {
Number { value: item }
}
}

fn main() {
let num = Number::from(30);
println!("My number is {:?}", num);
}

Into

Into 特征只是 From 特征的倒数。也就是说,如果您已经为您的类型实现了 From 特征, Into 将在必要时调用它。

使用 Into 特征通常需要指定要转换的类型,因为编译器大多数时候无法确定这一点。然而,考虑到我们免费获得该功能,这是一个小小的权衡。

use std::convert::Into;

#[derive(Debug)]
struct Number {
value: i32,
}

impl Into<Number> for i32 {
fn into(self) -> Number {
Number { value: self }
}
}

fn main() {
let int = 5;
// Try removing the type annotation
let num: Number = int.into();
println!("My number is {:?}", num);
}

TryFrom & TryTo

与 From 和 Into 类似, TryFrom 和 TryInto 是用于在类型之间进行转换的通用特征。与 From / Into 不同, TryFrom / TryInto 特征用于易出错的转换,因此返回 Result

use std::convert::TryFrom;
use std::convert::TryInto;

#[derive(Debug, PartialEq)]
struct EvenNumber(i32);

impl TryFrom<i32> for EvenNumber {
type Error = ();

fn try_from(value: i32) -> Result<Self, Self::Error> {
if value % 2 == 0 {
Ok(EvenNumber(value))
} else {
Err(())
}
}
}

fn main() {
// TryFrom

assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8)));
assert_eq!(EvenNumber::try_from(5), Err(()));

// TryInto

let result: Result<EvenNumber, ()> = 8i32.try_into();
assert_eq!(result, Ok(EvenNumber(8)));
let result: Result<EvenNumber, ()> = 5i32.try_into();
assert_eq!(result, Err(()));
}

toString

将任何类型转换为 String 就像实现该类型的 ToString 特征一样简单。您应该实现 fmt::Display 特征,而不是直接这样做,它会自动提供 ToString ,并且还允许打印 print! 部分中讨论的类型。

use std::fmt;

struct Circle {
radius: i32
}

impl fmt::Display for Circle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Circle of radius {}", self.radius)
}
}

fn main() {
let circle = Circle { radius: 6 };
println!("{}", circle.to_string());
}

解析字符串

将字符串转换为多种类型很有用,但更常见的字符串操作之一是将它们从字符串转换为数字。惯用的方法是使用 parse 函数,并安排类型推断或使用“turbofish”语法指定要解析的类型。以下示例显示了两种替代方案。

只要为该类型实现了 FromStr 特征,就会将字符串转换为指定的类型。标准库中的许多类型都实现了这一点。要在用户定义的类型上获得此功能,只需实现该类型的 FromStr 特征即可。

fn main() {
let parsed: i32 = "5".parse().unwrap();
let turbo_parsed = "10".parse::<i32>().unwrap();

let sum = parsed + turbo_parsed;
println!("Sum: {:?}", sum);
}
Loading Comments...