Rust学习之旅——泛型

前言

泛型在 Rust 中非常重要。他们可用于表示可空值(即可能还有值的变量)、错误处理、集合等等。下面展示经常使用的基础泛型知识。

泛型是什么

泛型允许我们不完全定义一个 struct 或者 enum ,使编译器能够根据我们的代码使用情况,在编译时创建一个完全定义的版本。

Rust 通常可以通过查看我们的实例化来推断最终的类型,但是如果需要帮助,可以使用 :: 操作符来显式地操作,该操作符也被称为 turbofish

// 一个部分定义的结构体
struct BagOgHolding<T> {
	item: T,
}

fn main() {
	// 注意:通过使用泛型,我们创建了编译时创建的类型,是代码更大
	// Turbofish 使其显式化
	let i32_bag = BagOfHolding::<i32> { item: 42 };
	let bool_bag = BagOfHolding::<bool> { item: true };
	// Rust也可以推断出泛型的类型!
	let float_bag = BagOfHolding { item: 3.14 };
	let bag_in_bag = BagOfHolding {
		item: BagOfHolding { item: "嘭! " },
	};
	
	println!("{} {} {} {}", i32_bag.item, bool_bag.item, float_bag.item, bag_in_bag.item.item);
}

表示空

在其他语言中,关键字 null 用于表示没有值。他给编程语言带来了困难,因为它使我们的程序在与变量字段交互时可能会失败。
Rust没有 null ,但是为一个或者多个代替值提供 None 替代的模式非常常见,而泛型有助于解决这一问题。

enum Item {
	Inventory(String),
	// None represents the absence of an item
	None,
}

struct BagOfHolding {
	item: Item,
}

Option

Rust 有一个内置的泛型枚举叫做 Option ,它可以让我们不使用 null 就可以表示空值。

enum Option<T> {
	None,
	Some(T),
}

这个枚举很常见,使用关键字 Some 和 None 可以在任何地方创建其实例。

// 一个部分定义的结构体
struct BagOfHolding<T> {
	// 我们的参数类型T可以传递给其他
	item: Option<T>,
}

fn main() {
	// 注意: 放一个 i32 的 bag ,里面什么都没有!
	// 我们必须注明类型, 否则 Rust 不知道 bag 的类型
	let i32_bag = BagOfHolding::<i32> { item: None };
	
	if i32_bag.item.is_none() {
		println!("there's nothing in the bag!");
	} else {
		println!("there's something in the bag1")}
	
	let i32_bag = BagOfHolding::<i32> { item: Some(42) };
	if i32_bag.item.is_some() {
		println!("there's something in the bag!");
	} else {
		println!("there's nothing in the bag!");
	}

	// match 可以让我们优雅的解构 Option ,并且确保我们处理了所有的可能情况!
	match i32_bag.item {
		Some(v) => println!("found {} in the bag!", v),
		None => println!("found nothing"),
	}
}

Result

Rust 有一个内置的泛型叫做 Result ,它可以让我们返回一个可能包含错误的值。这是编程语言进行错误处理的惯用方法。

enum Result<T, E> {
	Ok(T),
	Err(F),
}

注意我们的泛型有多个用逗号分隔的参数化的类型。

这个枚举很常见,使用关键词 Ok 和 Err 可以在任何地方创建其实例。

fn do_something_that_might_fail(i:i32) -> Result<f32, String> {
	if i == 42 {
		Ok(42.0)
	} else {
		Err(String::from("this is not the right number"))
	}
}

fn main() {
	let result = do_something_that_might_fail(12);

	// match 让我们可以优雅的解构 Rust,并且确保我们处理了所有的情况
	match result {
		Ok(v) => println!("found {}", v),
		Err(e) => println!("Error: {}", e),
	}
}

可失败的主函数

main 函数有可以返回 Result 的能力!

fn do_something_that_might_fail(i:i32) -> Result<f32, String> {
	if i == 42 {
		Ok(13.0)
	} else {
		Err(String::from("this is not the right number"))
	}
}


// 主函数不返回值,但是可以返回一个错误!
fn main() {
	let result = do_something_that_might_fail(12);

	match result {
		Ok(v) => println!("found {}", v),
		Err(e) => {
			// 如何优雅的处理错误
			// 返回一个说明发生了什么的新错误!
			return Err(String::from("something were wrong in main!"));
		},
	}
	Ok(())
}

优雅地错误处理

Result 如此常见以至于Rust有个强大的操作符 ? 来与之匹配。
以下两个表达式是等价的:

do_something_that_might_fail()?
match do_something_that_might_fail() {
	Ok(v) => v,
	Err(e) => return Err(e),
}

实例:

fn do_something_that_might_fail(i:i32) -> Result<f32, String> {
	if i == 42 {
		Ok(13.0)
	} else {
		Err(String::from("this is not the right number"))
	}
}


// 主函数不返回值,但是可以返回一个错误!
fn main() {
	// 可以比较下节省了多少代码
	let v = do_something_that_might_fail(42)?;
	println!("found {}", v);
	Ok(())
}

丑陋的 Option/Result 处理

当我们只是想试图快速的写一些代码时, Option/Result 对付起来都是有点无聊。这时候 Option/Result 都有一个名为 unwrap 的函数:这个函数可以简单粗暴地获取其中的值。
unwrap 会:

  • 获取 Option/Result 的值
  • 如果枚举类型是 None/Err,则会 panic!

这两段代码是等价的:

my_option.unwrap()
match my_option() {
	Some(v) => v,
	None => panic!("something error message generated"),
}

类似的:

my_result.unwrap()
match my_result() {
	Ok(v) => v,
	Err(e) => panic!("something error message generated: {}", e),
}

不过做个好 Rustacean(发音rus da’ tions),正确的使用 match !
实例:

fn do_something_that_might_fail(i:i32) -> Result<f32, String> {
	if i == 42 {
		Ok(13.0)
	} else {
		Err(String::from("this is not the right number"))
	}
}


fn main() {
	// 简洁但是假设性很强,而且很快就会变得丑陋
	let v = do_something_that_might_fail(42).unwrap();
	println!("found {}", v);

	// 这会 panic !
	let e = do_something_that_might_fail(1).unwrap();
	println!("found {}", e);

	Ok(())
}

Vectors

一些经常使用的泛型是集合类型。一个 vector 是可变长度的元素集合,以 Vec 结构表示。
比起手动构建,宏 Vec! 让我们可以轻松船舰 vector

Vec 有一个形如 iter() 的方法可以为一个 vector 创建迭代器,这允许我们轻松地将 vector 用到 for 循环中去。

内存细节:

  • Vec 是一个结构体, 但是内部其实都保存了在堆上固定长度数据的引用。
  • 一个 vector 开始有默认大小容量,当更多元素被添加进来后,它会重新在堆上分配一个新的并具有更大容量的定长列表。(类似C++的vector)

总结

这一章中,我们了解泛型带来的强大功能。下一章了解另外一个重要概念:数据所有权。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值