rust的类型转换和一些智能指针用法(四)

作者 : admin 本文共5933个字,预计阅读时间需要15分钟 发布时间: 2024-06-10 共2人阅读

基础类型

使用 as 关键字:

用于基本数值类型之间的转换,例如将 i32 转换为 u32。
例子:let x: i32 = 10; let y: u64 = x as u64;
使用标准库中的转换方法:

如 from()into() 方法,这些方法通常用于无风险的转换,或者当转换可能失败时返回 Result 类型。
例子:let x: i32 = 10; let y: u64 = u64::from(x);(如果转换是安全的)
使用 try_into()try_from():

当转换有可能失败时(例如,当试图将较大的整数类型转换为较小的整数类型时),这些方法会返回一个 Result 类型,允许错误处理。
例子:let x: i32 = 10; let y: u8 = x.try_into().unwrap();
布尔类型转换
布尔类型不能直接转换为数值类型,也不能从数值类型直接转换。
如果需要基于布尔值生成数值,可以使用条件表达式:let num = if bool_val { 1 } else { 0 };
字符与数值类型转换
字符类型 (char) 与整数类型之间的转换也需要显式操作。
例子:将 char 转换为其对应的 Unicode 编码(一个整数):let num = 'a' as u32;
从整数转换到 char 时需要保证整数是一个有效的 Unicode 码点:let ch = std::char::from_u32(97).unwrap();
元组和数组
元组和数组的转换通常涉及到元素的解构或重新组合,而不是类型转换。
例如,从元组转换到不同类型的元组或提取元组中的值:let tup = (1, 2.0, 'a'); let (x, y, z) = tup;

其他类型转字符串

1.使用 to_string() 方法
这是转换任何实现了 Display trait 的类型到字符串的最简单和最直接的方法。i32 和 f64 都实现了 Display trait,所以可以直接使用 to_string() 方法。

let num_i32 = 32;
let num_f64 = 10.5;

let str_from_i32 = num_i32.to_string();
let str_from_f64 = num_f64.to_string();

println!("i32 to String: {}", str_from_i32);  // 输出:i32 to String: 32
println!("f64 to String: {}", str_from_f64);  // 输出:f64 to String: 10.5

2.使用 format! 宏

let num_i32 = 32;
let num_f64 = 10.5;

// 使用 format! 宏进行基本转换
let str_from_i32 = format!("{}", num_i32);
let str_from_f64 = format!("{}", num_f64);

// 使用 format! 宏指定浮点数的精度
let formatted_f64 = format!("{:.2}", num_f64);

println!("i32 to String: {}", str_from_i32);  // 输出:i32 to String: 32
println!("f64 to String: {}", str_from_f64);  // 输出:f64 to String: 10.5
println!("Formatted f64: {}", formatted_f64);  // 输出:Formatted f64: 10.50

字符串转整形,或者浮点型

let s = "42".to_string();
let result: Result<i32, _> = s.parse();
match result {
    Ok(num) => println!("Converted string to i32: {}", num),
    Err(e) => println!("Failed to convert string to i32: {}", e),
}


let s = "3.14".to_string();
let result: Result<f64, _> = s.parse();
match result {
    Ok(num) => println!("Converted string to f64: {}", num),
    Err(e) => println!("Failed to convert string to f64: {}", e),
}



Result 和 Option 核心枚举类型常用的方法

Option
Option<T>
Option<T> 类型用于可能存在或可能不存在的值。它有两个变体:Some(T) 表示有一个值,和 None 表示没有值。

常用方法:

unwrap():提取 Some 的值或在 None 时引发 panic。
let some_option = Some("Hello");
println!("{}", some_option.unwrap());  // 输出 "Hello"

unwrap_or():提取 Some 的值或在 None 时返回一个默认值。
let none_option: Option<&str> = None;
println!("{}", none_option.unwrap_or("Default"));  // 输出 "Default"

map():如果是 Some(T),应用一个函数到内部值并返回一个新的 Optionlet num_option = Some(5);
let squared = num_option.map(|x| x * x);
println!("{:?}", squared);  // 输出 Some(25)


and_then():如果是 Some(T),则应用一个返回 Option<U> 的函数,否则返回 None
let some_string = Some("5");
let parsed = some_string.and_then(|s| s.parse::<i32>().ok());
println!("{:?}", parsed);  // 输出 Some(5)
Result
unwrap():提取 Ok 的值或在 Err 时引发 panic。
let ok_result: Result<i32, &str> = Ok(10);
println!("{}", ok_result.unwrap());  // 输出 10
unwrap_or():提取 Ok 的值或在 Err 时返回一个默认值。
let err_result: Result<i32, &str> = Err("error");
println!("{}", err_result.unwrap_or(0));  // 输出 0
map():如果是 Ok(T),应用一个函数到内部值并返回一个新的 Resultlet ok_result = Ok(2);
let doubled = ok_result.map(|x| x * 2);
println!("{:?}", doubled);  // 输出 Ok(4)
and_then():如果是 Ok(T),则应用一个返回 Result<U, E> 的函数,否则返回 Err(E)
let ok_result = Ok("10");
let parsed = ok_result.and_then(|s| s.parse::<i32>().map_err(|e| "parse error"));
println!("{:?}", parsed);  // 输出 Ok(10)
or_else():如果是 Err(E),应用一个函数来创建一个新的 Result,否则保持 Ok(T)
let err_result: Result<i32, &str> = Err("error");
let fixed = err_result.or_else(|_| Ok(0));
println!("{:?}", fixed);  // 输出 Ok(0)
if let
let some_option = Some(10);
if let Some(value) = some_option {
println!("Got a value: {}", value);
} else {
println!("Got nothing!");
}
let result: Result<i32, String> = Ok(20);
if let Ok(num) = result {
println!("Success with number: {}", num);
} else {
println!("Failed!");
}
let some_string = Some("Hello, World!");
if let Some(length) = some_string.map(|s| s.len()) {
println!("The string length is: {}", length);
} else {
println!("No string!");
}
let result: Result<i32, String> = Err("Something went wrong".to_string());
if let Err(e) = result {
println!("Error occurred: {}", e);
}
// 也可以添加一个 else 分支处理成功情况
else {
println!("Success, no errors!");
}
let result: Result<i32, String> = Err("failed".to_string());
// 使用 map_err 转换错误类型,然后使用 if let 检查
if let Err(e) = result.map_err(|e| format!("Error: {}", e)) {
println!("{}", e);
}

常用智能指针

Box 在 Rust 中是一个非常有用的类型,它允许你将数据放在堆上而不是栈上

常用的几个场景

1. 处理大数据
当你有一个非常大的数据结构时,将其放在栈上可能会导致栈溢出或不必要的性能负担。使用 Box 可以避免这些问题,因为它将数据存储在堆上。
示例:
let large_array = Box::new([0u8; 1000000]);  // 1MB的空间
这里,一个大型的数组被分配在堆上,而不是占用宝贵的栈空间。
2. 递归类型
在定义递归数据结构时,比如链表或树,你通常需要使用 Box 来间接持有类型的一部分。这是因为 Rust 需要在编译时知道类型的确切大小,而递归类型无法在不进行某种形式的间接寻址的情况下给出确切大小。
示例:
enum List<T> {
Cons(T, Box<List<T>>),
Nil,
}
use List::{Cons, Nil};
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
这里的链表 List 使用 Box 来持有其递归部分,这使得 Rust 能够处理变量大小的类型。
3. 确保类型拥有 trait 对象
当你需要创建一个动态分发的类型时,通常需要使用 trait 对象。Trait 对象(如 Box<dyn SomeTrait>)允许你在运行时存储和调用实现了该 trait 的任何类型的实例。
示例:
trait Speak {
fn speak(&self);
}
struct Dog;
struct Cat;
impl Speak for Dog {
fn speak(&self) {
println!("Woof!");
}
}
impl Speak for Cat {
fn speak(&self) {
println!("Meow!");
}
}
let animals: Vec<Box<dyn Speak>> = vec![
Box::new(Dog),
Box::new(Cat),
];
for animal in animals {
animal.speak();
}
在这个示例中,不同类型的动物都实现了 Speak trait,并且通过 Box<dyn Speak> 存储在同一个向量中,使得可以在运行时动态地调用它们的 speak 方法。
4. 接口的强制所有权
有时,你可能想要确保某个函数完全拥有它接收的数据,Box<T> 可以用来明确这种所有权转移。
示例:
fn process_data(data: Box<Data>) {
// 处理数据
}
let data = Box::new(Data::new());
process_data(data);
RefCell 与 Rc 的使用

这种组合允许在单线程环境中实现多个可变引用的共享数据。Rc 本身不允许可变引用,因为它只保证数据的多所有者共享,而不提供数据修改的能力。结合 RefCell,它提供了一种运行时检查的方式来借用可变或不可变的引用。

示例:使用 RefCell 与 Rc
假设我们有一个结构体 Person,它存储在多个地方,并且我们希望在运行时根据需要修改其字段:

use std::rc::Rc;
use std::cell::RefCell;
struct Person {
name: String,
age: u32,
}
fn main() {
let person = Rc::new(RefCell::new(Person {
name: "Alice".to_string(),
age: 30,
}));
{
let mut p = person.borrow_mut();
p.age += 1;
}
{
let p = person.borrow();
println!("{} is {} years old.", p.name, p.age);
}
}

例子2 ,共享多个写入和读取

use std::rc::Rc;
use std::cell::RefCell;
struct TextBox {
content: Rc<RefCell<String>>,
}
impl TextBox {
fn new(content: &Rc<RefCell<String>>) -> TextBox {
TextBox {
content: Rc::clone(content),
}
}
fn display(&self) {
println!("{}", self.content.borrow());
}
fn append(&self, text: &str) {
println!("Before append: {}", self.content.borrow());
self.content.borrow_mut().push_str(text);
println!("After append: {}", self.content.borrow());
}
}
fn main() {
let shared_text = Rc::new(RefCell::new(String::from("Hello")));
let text_box1 = TextBox::new(&shared_text);
text_box1.append(" world");
text_box1.append(" gitxuzan");
let text_box2 = TextBox::new(&shared_text);
text_box2.display(); // 应该输出 "Hello world gitxuzan"
}
本站无任何商业行为
个人在线分享 » rust的类型转换和一些智能指针用法(四)
E-->