【Rust学习】5. Struct

文章介绍了Rust中的struct定义与实例化,包括如何创建和使用struct,struct的字段访问,以及struct作为函数返回值。还展示了struct方法的定义,如`area()`方法,并通过Rectangle结构体计算长方形面积的例子说明struct在组合数据和提高代码可读性上的作用。

5.1 定义并实例化struct

什么是 struct

  • struct,结构体
    • 自定义的数据类型(类似Java对象)
    • 为相关联的值命名,打包 => 有意义的组合

定义 struct

  • 使用 struct 关键字,并为整个 struct 命名
  • 在花括号内,为所有字段 (Field) 定义名称和类型
  • 例如:
struct User{
    username:String,
    email:String,
    sign_in_count:u64,
    active:bool,
}

实例化 struct

  • 想要使用 struct,需要创建 struct 的实例:
    • 为每个字段指定具体值
    • 无需按声明的顺序进行指定
  • 例子
    必须全部赋值
fn main() {
   let user1=User{
    email: String::from("2131@qq.com"),
    username:String::from("Kiki"),
    active:true,
    sign_in_count:556,
   };

}
struct User{
    username:String,
    email:String,
    sign_in_count:u64,
    active:bool,
}

取得 struct 里面的某个值

  • 使用点标记法:
  • 例子
    在这里插入图片描述
fn main() {
   let mut user1=User{
    email: String::from("2131@qq.com"),
    username:String::from("Kiki"),
    active:true,
    sign_in_count:556,
   };
   user1.email=String::from("ano@ex.com");
}

注意
一旦 struct 的实例是可变的,那么实例中所有的字段都是可变的

struct 作为函数的返回值

fn build_user(email:String, username:String)->User{
    User{
        email: email,
        username:username,
        active:true,
        sign_in_count:556,
    }
}

字段初始化简写

  • 当字段名与字段值对应变量名相同时,就可以使用字段初始化简写的方式:
fn build_user(email:String, username:String)->User{
    User{
        email,
        username,
        active:true,
        sign_in_count:556,
    }
}

struct 更新语法

  • 当你想基于某个 struct 实例来创建一个新实例的时候,可以使用 struct 更新语法
   let mut user2=User{
    email: String::from("2131@qq.com"),
    username:String::from("Kiki"),
    active:user1.active,
    sign_in_count:user1.sign_in_count,
   };
   let mut user3=User{
    email: String::from("2131@qq.com"),
    ..user1
   };

Tuple struct

  • 可定义类似 tuple的 struct,叫做 tuple struct
  • Tuple struct 整体有个名,但里面的元素没有名
  • 适用:想给整个 tuple 起名,并让它不同于其它 tuple,而且又不需要给每个元素起名
  • 定义 tuple struct: 使用 struct 关键字,后边是名字,以及里面元素的类型
  • 例子:
    struct Color(i32, i32, i32);
    struct Point(i32, i32, i32)
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
  • black 和 origin 是不同的类型,是不同 tuple struct 的实例
    Unit-Like Struct (没有任何字段)
  • 可以定义没有任何字段的 struct,叫做 Unit-Like struct(因为与(),单元类型类似)
  • 适用于需要在某个类型上实现某个 trcit,但是在里面又没有想要存储的数据

struct 数据的所有权

struct User{
    username:String,
    email:String,
}
  • 这里的字段使用了 string 而不是 &str:
    -该 struct 实例拥有其所有的数据
    • 只要 struct 实例是有效的,那么里面的字段数据也是有效的
  • struct 里也可以存放引用,但这需要使用生命周期(以后讲)
    • 生命周期保证只要 struct 实例是有效的,那么里面的引用也是有效的。
    • 如果 struct 里面存储引用,而不使用生命周期,就会报错(例子)

在这里插入图片描述

5.2 struct例子

需求:计算长方形面积

fn main(){
    let w=30;
    let l=50;

    println!("area={}",area(w, l))
}
fn area(width:u32, length:u32)->u32{
    width*length
}

问题:传进来的长和宽没有关联,要怎么组合到一起呢?

用元组

fn main(){
    let rect=(30,50);
    println!("area={}",area(rect))
}
fn area(dim:(u32,u32))->u32{
    dim.0*dim.1
}

可读性差,不知到哪个长哪个宽,用对象解决试试

fn main(){
    let rect=Rectangle{
        width:30,
        length:50,
    };
    println!("{}",area(&rect))
}
fn area(rect:&Rectangle)->u32{
   rect.width*rect.length
}
struct Rectangle{
    width:u32,
    length:u32,
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

{:?}

#[derive(Debug)]
struct Rectangle{
    width:u32,
    length:u32,
}
fn main(){
    let rect=Rectangle{
        width:30,
        length:50,
    };
    println!("{:?}",rect);
}

Rectangle { width: 30, length: 50 }

{:#?}

#[derive(Debug)]
struct Rectangle{
    width:u32,
    length:u32,
}
fn main(){
    let rect=Rectangle{
        width:30,
        length:50,
    };
    println!("{:#?}",rect);
}

Rectangle {
    width: 30,
    length: 50,
}

在这里插入图片描述

5.3 struct的方法

定义方法

  • 方法和函数类似: fn 关键字、名称、参数、返回值
  • 方法与函数不同之处:
    • 方法是在 struct (或enum、trait 对象)的上下文中定义
    • 第一个参数是 self,表示方法被调用的 struct 实例
impl Rectangle{
    fn area(&self)->u32{
        self.width*self.length
    }
}

#[derive(Debug)]
struct Rectangle{
    width:u32,
    length:u32,
}
impl Rectangle{
    fn area(&self)->u32{
        self.width*self.length
    }
}
fn main(){
    let rect=Rectangle{
        width:30,
        length:50,
    };
    println!("{}",rect.area());
}

  • 在impl 块里定义方法
  • 方法的第一个参数可以是 &self,也可以获得其所有权 或 可变借用。和其他参数一样。
  • 更良好的代码组织。

在这里插入图片描述

方法参数

方法可以有多个参数

#[derive(Debug)]
struct Rectangle{
    width:u32,
    length:u32,
}
impl Rectangle{
    fn area(&self)->u32{
        self.width*self.length
    }
    fn can_hold(&self,other:&Rectangle)->bool{
        self.width>other.width && self.length>other.length
    }
}
fn main(){
    let rect1=Rectangle{
        width:30,
        length:50,
    };
    let rect2=Rectangle{
        width:70,
        length:40,
    };
    println!("{}",rect1.can_hold(&rect2));
}

关联函数

  • 可以在 impl 块里定义不把 self 作为第一个参数的函数,它们叫关联函数 (不是方)
    • 例如: String::from()
  • 关联函数通常用于构造器(例子)
  • :: 符号
    • 关联函数
    • 模块创建的命名空间
#[derive(Debug)]
struct Rectangle{
    width:u32,
    length:u32,
}
impl Rectangle{
    fn area(&self)->u32{
        self.width*self.length
    }
    fn can_hold(&self,other:&Rectangle)->bool{
        self.width>other.width && self.length>other.length
    }
    fn square(size:u32)->Rectangle{
        Rectangle { 
            width: size, 
            length: size 
        }
    }
}
fn main(){
    let s=Rectangle::square(20);
}

多个impl 块

  • 每个struct 允许拥有多个impl块
<think> 我们将在Rust中搭建whisper.cpp的开发环境。whisper.cpp是Whisper模型的C++实现,而我们将使用其Rust绑定库whisper-rs。 步骤包括:安装依赖、配置环境、下载模型、编写示例代码。 环境要求: 1. Rust 工具链(建议使用最新稳定版) 2. C++编译器(用于编译whisper.cpp) 3. CMake(构建工具) 4. 系统依赖(如pkg-config, libclang等) 详细步骤: 1. 创建新项目 2. 添加依赖 3. 编写示例代码 4. 下载模型 5. 运行 下面我们逐步进行: </think># 搭建 Rust 的 whisper.cpp 开发环境 下面我将详细介绍如何在 Rust 中搭建 whisper.cpp 的开发环境,包括系统依赖安装、模型管理、项目配置以及核心功能实现。 ## 环境搭建步骤 ### 1. 系统依赖安装 ```bash # Ubuntu/Debian sudo apt update sudo apt install -y build-essential cmake pkg-config libclang-dev llvm-dev # macOS brew update brew install cmake llvm pkg-config # Windows (使用MSVC) choco install cmake llvm visualstudio2022-workload-vctools ``` ### 2. Rust 工具链配置 ```bash # 安装 Rust (如果尚未安装) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source $HOME/.cargo/env # 添加必要的 Rust 组件 rustup target add wasm32-unknown-unknown cargo install cargo-edit ``` ### 3. 创建项目并添加依赖 ```bash cargo new rust_whisper cd rust_whisper # 添加 whisper-rs 和其他必要依赖 cargo add whisper-rs --features download,metal # macOS 启用 Metal 支持 cargo add anyhow cpal hound tokio rodio # 音频处理和错误处理 cargo add tauri --optional # 可选:桌面应用框架 cargo add actix-web --optional # 可选:Web服务框架 ``` ### 4. 配置 Cargo.toml ```toml [package] name = "rust_whisper" version = "0.1.0" edition = "2021" [features] default = ["metal"] metal = ["whisper-rs/metal"] # macOS Metal 加速 cuda = [] # 需要手动编译支持 [dependencies] whisper-rs = { version = "0.5", features = ["download"] } anyhow = "1.0" cpal = "0.15" hound = "3.5" tokio = { version = "1", features = ["full"] } rodio = "0.17" # 音频播放 serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" # 可选依赖 tauri = { version = "1", optional = true } actix-web = { version = "4", optional = true } [build-dependencies] cc = "1.0" ``` ## 模型管理 ### 1. 创建模型目录 ```bash mkdir -p models ``` ### 2. 模型下载脚本 (download_models.sh) ```bash #!/bin/bash # 基础英语模型 wget -P models https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin # 多语言模型 wget -P models https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.bin # 量化模型 (推荐) wget -P models https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en-q5_0.bin wget -P models https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium-q5_0.bin ``` ### 3. 模型选择建议 | 模型名称 | 大小 | 内存占用 | 适用场景 | |----------|------|----------|----------| | ggml-tiny.en | 75 MB | ~100 MB | 嵌入式设备、实时识别 | | ggml-base.en | 142 MB | ~200 MB | 平衡速度与精度 | | ggml-medium | 1.5 GB | ~2 GB | 高精度多语言转录 | | ggml-*.q5_0 | 40-400 MB | 减少30-50% | 推荐生产环境使用 | ## 核心功能实现 ### 1. 模型加载与初始化 ```rust // src/lib.rs use whisper_rs::{WhisperContext, WhisperState}; use anyhow::{Result, Context}; pub struct WhisperModel { ctx: WhisperContext, state: WhisperState, } impl WhisperModel { pub fn new(model_path: &str) -> Result<Self> { let ctx = WhisperContext::new(model_path) .with_context(|| format!("Failed to load model from {}", model_path))?; let state = ctx.create_state() .context("Failed to create inference state")?; Ok(Self { ctx, state }) } pub fn transcribe(&mut self, audio_data: &[f32], params: &whisper_rs::FullParams) -> Result<String> { self.state.full(params, audio_data) .context("Transcription failed")?; let num_segments = self.state.full_n_segments() .context("Failed to get segment count")?; let mut transcript = String::new(); for i in 0..num_segments { let segment = self.state.full_get_segment_text(i) .context("Failed to get segment text")?; transcript.push_str(&segment); } Ok(transcript) } } ``` ### 2. 音频文件处理 ```rust // src/audio.rs use hound::{WavReader, WavSpec}; use anyhow::{Result, Context}; pub fn read_wav_file(path: &str) -> Result<Vec<f32>> { let mut reader = WavReader::open(path) .with_context(|| format!("Failed to open WAV file: {}", path))?; let spec = reader.spec(); validate_audio_spec(&spec)?; let samples: Vec<i16> = reader.samples::<i16>() .map(|s| s.unwrap()) .collect(); // 转换为f32并归一化 let audio_data: Vec<f32> = samples .iter() .map(|s| (*s as f32) / 32768.0) .collect(); Ok(audio_data) } fn validate_audio_spec(spec: &WavSpec) -> Result<()> { if spec.channels != 1 { anyhow::bail!("Audio must be mono (1 channel). Found {} channels.", spec.channels); } if spec.sample_rate != 16000 { anyhow::bail!("Sample rate must be 16000 Hz. Found {} Hz.", spec.sample_rate); } if spec.bits_per_sample != 16 { anyhow::bail!("Bits per sample must be 16. Found {}.", spec.bits_per_sample); } Ok(()) } ``` ### 3. 实时语音识别 ```rust // src/realtime.rs use cpal::{traits::*, Stream, StreamConfig}; use whisper_rs::convert_stereo_to_mono_audio; use anyhow::{Result, Context}; use tokio::sync::mpsc; pub async fn start_realtime_recognition( model: &mut whisper_rs::WhisperState, sample_rate: u32, channels: u16 ) -> Result<mpsc::Receiver<String>> { let host = cpal::default_host(); let device = host.default_input_device() .context("No input device available")?; let config = StreamConfig { channels, sample_rate: cpal::SampleRate(sample_rate), buffer_size: cpal::BufferSize::Fixed(1024), }; let (tx, rx) = mpsc::channel(10); let mut audio_buffer = Vec::new(); let state = model.clone(); // 使用Arc/Mutex在实际应用中 let stream = device.build_input_stream( &config, move |data: &[f32], _| { let mono_audio = if channels == 2 { convert_stereo_to_mono_audio(data) } else { data.to_vec() }; audio_buffer.extend(mono_audio); // 每2秒处理一次 if audio_buffer.len() >= (sample_rate * 2) as usize { let buffer_copy = audio_buffer.drain(..).collect::<Vec<_>>(); // 在实际应用中应使用线程池 let tx_clone = tx.clone(); tokio::spawn(async move { let mut params = whisper_rs::FullParams::new( whisper_rs::SamplingStrategy::Greedy { best_of: 1 } ); params.set_print_realtime(true); let result = state.full(¶ms, &buffer_copy); if let Ok(_) = result { let transcript = (0..state.full_n_segments().unwrap()) .map(|i| state.full_get_segment_text(i).unwrap()) .collect::<String>(); let _ = tx_clone.send(transcript).await; } }); } }, |err| eprintln!("Audio stream error: {:?}", err), None )?; stream.play()?; // 在实际应用中应保存stream以保持活动状态 Ok(rx) } ``` ### 4. 配置管理 ```rust // src/config.rs use serde::{Deserialize, Serialize}; use std::path::PathBuf; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct WhisperConfig { pub model_path: PathBuf, pub language: Option<String>, pub translate: bool, pub temperature: f32, pub threads: usize, } impl Default for WhisperConfig { fn default() -> Self { Self { model_path: PathBuf::from("models/ggml-base.en-q5_0.bin"), language: Some("auto".to_string()), translate: false, temperature: 0.0, threads: num_cpus::get_physical() / 2, } } } pub fn load_config() -> Result<WhisperConfig, std::io::Error> { let config_path = dirs::config_dir() .unwrap_or_else(|| PathBuf::from(".")) .join("rust_whisper") .join("config.toml"); if config_path.exists() { let content = std::fs::read_to_string(config_path)?; Ok(toml::from_str(&content)?) } else { let config = WhisperConfig::default(); save_config(&config)?; Ok(config) } } pub fn save_config(config: &WhisperConfig) -> Result<(), std::io::Error> { let config_dir = dirs::config_dir() .unwrap_or_else(|| PathBuf::from(".")) .join("rust_whisper"); std::fs::create_dir_all(&config_dir)?; let config_path = config_dir.join("config.toml"); let content = toml::to_string_pretty(config)?; std::fs::write(config_path, content)?; Ok(()) } ``` ## 完整示例应用 ### 命令行转录工具 ```rust // src/main.rs mod lib; mod audio; mod config; mod realtime; use anyhow::Result; use lib::WhisperModel; use audio::read_wav_file; use config::WhisperConfig; use whisper_rs::{FullParams, SamplingStrategy}; #[tokio::main] async fn main() -> Result<()> { let config = config::load_config()?; // 初始化模型 let mut model = WhisperModel::new(&config.model_path.to_string_lossy())?; // 文件转录模式 if let Some(file_path) = std::env::args().nth(1) { let audio_data = read_wav_file(&file_path)?; let mut params = FullParams::new(SamplingStrategy::Greedy { best_of: 1 }); params.set_language(config.language.as_deref()); params.set_translate(config.translate); params.set_temperature(config.temperature); let transcript = model.transcribe(&audio_data, &params)?; println!("转录结果:\n{}", transcript); } // 实时识别模式 else { println!("启动实时语音识别 (按Ctrl+C退出)..."); let (mut state, _) = model.get_state()?; // 需要修改WhisperModel以暴露状态 let rx = realtime::start_realtime_recognition(&mut state, 16000, 1).await?; while let Some(transcript) = rx.recv().await { println!("实时转录: {}", transcript); } } Ok(()) } ``` ## 性能优化技巧 ### 1. 启用硬件加速 ```rust // 在 macOS 上启用 Metal 加速 #[cfg(target_os = "macos")] { use whisper_rs::MetalContext; let ctx = MetalContext::new() .and_then(|metal| WhisperContext::new_with_metal(model_path, metal)) .context("Failed to create Metal context")?; } // 在支持 CUDA 的 Linux 系统上 #[cfg(target_os = "linux")] { // 需要手动编译支持 CUDA 的 whisper.cpp // 设置环境变量启用 CUDA std::env::set_var("WHISPER_CUBLAS", "1"); } ``` ### 2. 模型量化与内存优化 ```rust // 使用量化模型减少内存占用 let mut model = WhisperModel::new("models/ggml-base.en-q5_0.bin")?; // 限制内存使用 model.state.set_max_allocator_size(512 * 1024 * 1024); // 512MB // 使用流式处理长音频 fn stream_transcribe(model: &mut WhisperModel, audio: &[f32], chunk_size: usize) -> Result<String> { let mut transcript = String::new(); for chunk in audio.chunks(chunk_size) { let mut params = FullParams::new(SamplingStrategy::Greedy { best_of: 1 }); params.set_no_context(true); // 不使用之前的上下文 transcript.push_str(&model.transcribe(chunk, &params)?); } Ok(transcript) } ``` ## 测试与验证 ### 1. 单元测试 ```rust // tests/integration.rs #[cfg(test)] mod tests { use super::*; use tempfile::NamedTempFile; #[test] fn test_short_transcription() { // 创建临时 WAV 文件 let mut tmpfile = NamedTempFile::new().unwrap(); write_test_wav(tmpfile.path()); // 加载模型 let mut model = WhisperModel::new("models/ggml-tiny.en.bin").unwrap(); // 读取音频 let audio = read_wav_file(tmpfile.path().to_str().unwrap()).unwrap(); // 转录 let mut params = FullParams::new(SamplingStrategy::Greedy { best_of: 1 }); let transcript = model.transcribe(&audio, &params).unwrap(); // 验证 assert!(transcript.to_lowercase().contains("hello")); } fn write_test_wav(path: &Path) { // 实现创建测试音频文件的代码 } } ``` ### 2. 性能基准测试 ```rust // benches/transcription_bench.rs #![feature(test)] extern crate test; use test::Bencher; use whisper_rs::{WhisperContext, FullParams, SamplingStrategy}; #[bench] fn bench_tiny_model(b: &mut Bencher) { let ctx = WhisperContext::new("models/ggml-tiny.en.bin").unwrap(); let state = ctx.create_state().unwrap(); let audio = vec![0.0; 16000 * 5]; // 5秒静音 b.iter(|| { let mut params = FullParams::new(SamplingStrategy::Greedy { best_of: 1 }); state.full(¶ms, &audio).unwrap(); }); } #[bench] fn bench_quantized_model(b: &mut Bencher) { let ctx = WhisperContext::new("models/ggml-base.en-q5_0.bin").unwrap(); // ... 同上 } ``` ## 部署方案 ### 1. 构建优化 ```bash # 发布构建 RUSTFLAGS="-C target-cpu=native" cargo build --release # 最小化二进制大小 cargo install cargo-strip cargo strip ``` ### 2. Docker 容器化 ```dockerfile FROM rust:1.70-slim-bullseye AS builder RUN apt update && apt install -y cmake clang WORKDIR /app COPY . . RUN cargo build --release FROM debian:bullseye-slim COPY --from=builder /app/target/release/rust_whisper /usr/local/bin COPY --from=builder /app/models /models CMD ["rust_whisper"] ``` ## 常见问题解决 1. **编译错误:找不到 whisper.h** - 解决方案:确保 `whisper.cpp` 子模块已初始化 ```bash git submodule update --init ``` 2. **内存不足错误** - 使用量化模型:`ggml-*.q5_0.bin` - 减少线程数:`model.state.set_threads(2)` - 限制音频长度 3. **音频处理问题** - 使用 FFmpeg 预处理音频: ```bash ffmpeg -i input.mp3 -ar 16000 -ac 1 output.wav ``` 4. **Metal 初始化失败 (macOS)** - 确认安装 Xcode 命令行工具:`xcode-select --install` - 更新 macOS 到最新版本 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值