Rust实现DAW时线程间消息传递的类型系统问题咨询
我用Rust自主实现一款DAW(数字音频工作站),在处理线程间消息传递时遇到类型系统相关问题。具体设置了两个线程:一个负责接收UI消息并转发给引擎,另一个负责运行引擎并处理消息。线程1发送消息后会等待线程2处理,根据消息类型可能返回对应结果。
每个创建的BusTrack对应一个MailMan和MailReceiver,UI可在线程1中调用MailMan的send_and_await_recv()方法,线程2中的每个BusTrack会通过MailReceiver检查新消息,无新消息时继续创建和处理音频。
我希望基于定义的BusTrackMessage枚举,让Rust编译器推断或检查线程间传递消息的正确输出类型,同时已定义OutputType枚举表示引擎可能返回的类型。当前简化代码如下:
pub enum BusTrackMessage { SetMidiTrack((MidiTrack, [type = Empty])), AdjustRelease((f64, [type = Empty])), GetRelease(((), [type = Value(f64)])), AdjustVolume((f64, [type = Empty])), GetVolume((Empty, [type = f64])), } pub enum OutputType { Value(f64), Empty, } pub struct MessageHolder { sender_back: Sender<OutputType>, message: BusTrackMessage, } impl MessageHolder { pub fn new(message: BusTrackMessage, sender_back: Sender<OutputType>) -> MessageHolder { MessageHolder { sender_back, message, } } } pub struct MailMan { sender: Sender<MessageHolder>, } impl MailMan { pub fn new() -> Self { let (sender, _receiver) = mpsc::channel(); MailMan { sender } } pub fn send_and_await_recv(&self, message: BusTrackMessage) -> <[outputtype based on message?]> { let (send_back, receiver_back) = mpsc::channel(); let message_holder = MessageHolder::new(message, send_back); self.sender.send(message_holder).unwrap(); receiver_back.recv().unwrap() } }
目前使用std::mpsc的Sender和Receiver实现线程间消息传递,但不确定如何根据传入的BusTrackMessage,为MailMan的send_and_await_recv函数定义正确的返回输出类型及通道类型。想知道是否有办法让Rust编译器基于BusTrackMessage枚举推断或检查正确的输出类型,或是有其他更好的实现方案?
方案一:用特质关联类型重构消息系统
核心是通过特质的关联类型绑定每个命令的输入和输出,让编译器静态推断返回类型,完全避免运行时类型错误。
1. 定义基础特质与类型
先定义空类型替代原OutputType::Empty,再定义命令特质:
use std::sync::mpsc; #[derive(Debug, Clone, Copy)] pub struct Empty; pub trait BusTrackCommand { type Input; type Output; // 命令执行逻辑,由BusTrack实现 fn execute(self, input: Self::Input, track: &mut BusTrack) -> Self::Output; } // 示例BusTrack结构体,根据实际业务补充字段 pub struct BusTrack { midi_track: Option<MidiTrack>, release: f64, volume: f64, }
2. 实现具体命令
把原枚举的每个变体拆成独立结构体,分别实现BusTrackCommand:
pub struct SetMidiTrack; impl BusTrackCommand for SetMidiTrack { type Input = MidiTrack; type Output = Empty; fn execute(self, input: Self::Input, track: &mut BusTrack) -> Self::Output { track.midi_track = Some(input); Empty } } pub struct AdjustRelease; impl BusTrackCommand for AdjustRelease { type Input = f64; type Output = Empty; fn execute(self, input: Self::Input, track: &mut BusTrack) -> Self::Output { track.release = input; Empty } } pub struct GetRelease; impl BusTrackCommand for GetRelease { type Input = (); type Output = f64; fn execute(self, _input: Self::Input, track: &mut BusTrack) -> Self::Output { track.release } } pub struct AdjustVolume; impl BusTrackCommand for AdjustVolume { type Input = f64; type Output = Empty; fn execute(self, input: Self::Input, track: &mut BusTrack) -> Self::Output { track.volume = input; Empty } } pub struct GetVolume; impl BusTrackCommand for GetVolume { type Input = (); type Output = f64; fn execute(self, _input: Self::Input, track: &mut BusTrack) -> Self::Output { track.volume } }
3. 泛型化消息传递系统
用枚举包裹不同类型的命令holder,让mpsc通道能接收统一类型,同时保留泛型的类型安全:
pub struct CommandHolder<C: BusTrackCommand> { sender_back: mpsc::Sender<C::Output>, command: C, input: C::Input, } impl<C: BusTrackCommand> CommandHolder<C> { pub fn new(command: C, input: C::Input, sender_back: mpsc::Sender<C::Output>) -> Self { Self { sender_back, command, input } } } pub enum AnyCommandHolder { SetMidiTrack(CommandHolder<SetMidiTrack>), AdjustRelease(CommandHolder<AdjustRelease>), GetRelease(CommandHolder<GetRelease>), AdjustVolume(CommandHolder<AdjustVolume>), GetVolume(CommandHolder<GetVolume>), } pub struct MailMan { sender: mpsc::Sender<AnyCommandHolder>, } impl MailMan { pub fn new(mut track: BusTrack) -> Self { let (sender, receiver) = mpsc::channel(); // 启动引擎线程处理命令 std::thread::spawn(move || { while let Ok(holder) = receiver.recv() { match holder { AnyCommandHolder::SetMidiTrack(h) => { let output = h.command.execute(h.input, &mut track); h.sender_back.send(output).unwrap(); } AnyCommandHolder::AdjustRelease(h) => { let output = h.command.execute(h.input, &mut track); h.sender_back.send(output).unwrap(); } AnyCommandHolder::GetRelease(h) => { let output = h.command.execute(h.input, &mut track); h.sender_back.send(output).unwrap(); } AnyCommandHolder::AdjustVolume(h) => { let output = h.command.execute(h.input, &mut track); h.sender_back.send(output).unwrap(); } AnyCommandHolder::GetVolume(h) => { let output = h.command.execute(h.input, &mut track); h.sender_back.send(output).unwrap(); } } } }); MailMan { sender } } pub fn send_and_await_recv<C: BusTrackCommand>(&self, command: C, input: C::Input) -> C::Output { let (send_back, receiver_back) = mpsc::channel(); let holder = CommandHolder::new(command, input, send_back); // 将泛型holder转为统一的AnyCommandHolder let any_holder = match holder { h @ CommandHolder { command: SetMidiTrack, .. } => AnyCommandHolder::SetMidiTrack(h), h @ CommandHolder { command: AdjustRelease, .. } => AnyCommandHolder::AdjustRelease(h), h @ CommandHolder { command: GetRelease, .. } => AnyCommandHolder::GetRelease(h), h @ CommandHolder { command: AdjustVolume, .. } => AnyCommandHolder::AdjustVolume(h), h @ CommandHolder { command: GetVolume, .. } => AnyCommandHolder::GetVolume(h), }; self.sender.send(any_holder).unwrap(); receiver_back.recv().unwrap() } }
调用示例
编译器会自动推断返回类型,完全类型安全:
let track = BusTrack { midi_track: None, release: 0.5, volume: 1.0 }; let mailman = MailMan::new(track); // 无返回值命令,编译器知道返回Empty mailman.send_and_await_recv(AdjustRelease, 1.0); // 有返回值命令,编译器自动推断返回f64 let current_release = mailman.send_and_await_recv(GetRelease, ()); let current_volume = mailman.send_and_await_recv(GetVolume, ());
方案二:保留枚举,用类型标记实现类型安全
如果不想拆分原枚举,可以用PhantomData标记每个变体的输出类型,结合枚举转换让编译器做静态检查。
1. 重构BusTrackMessage枚举
为每个变体添加输出类型标记:
use std::marker::PhantomData; use std::sync::mpsc; pub enum OutputType { Value(f64), Empty, } pub enum BusTrackMessage<O> { SetMidiTrack(MidiTrack, PhantomData<O>), AdjustRelease(f64, PhantomData<O>), GetRelease((), PhantomData<O>), AdjustVolume(f64, PhantomData<O>), GetVolume((), PhantomData<O>), } // 类型别名简化使用 pub type SetMidiTrackMsg = BusTrackMessage<OutputType>; pub type AdjustReleaseMsg = BusTrackMessage<OutputType>; pub type GetReleaseMsg = BusTrackMessage<f64>; pub type AdjustVolumeMsg = BusTrackMessage<OutputType>; pub type GetVolumeMsg = BusTrackMessage<f64>; // 示例BusTrack结构体 pub struct BusTrack { midi_track: Option<MidiTrack>, release: f64, volume: f64, }
2. 修改消息传递系统
用枚举包裹不同输出类型的消息holder,确保通道能接收统一类型:
pub struct MessageHolder<O> { sender_back: mpsc::Sender<O>, message: BusTrackMessage<O>, } impl<O> MessageHolder<O> { pub fn new(message: BusTrackMessage<O>, sender_back: mpsc::Sender<O>) -> Self { Self { sender_back, message } } } pub enum AnyMessageHolder { EmptyOutput(MessageHolder<OutputType>), F64Output(MessageHolder<f64>), } pub struct MailMan { sender: mpsc::Sender<AnyMessageHolder>, } impl MailMan { pub fn new(mut track: BusTrack) -> Self { let (sender, receiver) = mpsc::channel(); std::thread::spawn(move || { while let Ok(holder) = receiver.recv() { match holder { AnyMessageHolder::EmptyOutput(h) => { match h.message { BusTrackMessage::SetMidiTrack(midi, _) => { track.midi_track = Some(midi); h.sender_back.send(OutputType::Empty).unwrap(); } BusTrackMessage::AdjustRelease(val, _) => { track.release = val; h.sender_back.send(OutputType::Empty).unwrap(); } BusTrackMessage::AdjustVolume(val, _) => { track.volume = val; h.sender_back.send(OutputType::Empty).unwrap(); } _ => panic!("Invalid message type for EmptyOutput"), } } AnyMessageHolder::F64Output(h) => { match h.message { BusTrackMessage::GetRelease(_, _) => { h.sender_back.send(track.release).unwrap(); } BusTrackMessage::GetVolume(_, _) => { h.sender_back.send(track.volume).unwrap(); } _ => panic!("Invalid message type for F64Output"), } } } } }); MailMan { sender } } pub fn send_and_await_recv<O: Send + 'static>(&self, message: BusTrackMessage<O>) -> O { let (send_back, receiver_back) = mpsc::channel(); let holder = MessageHolder::new(message, send_back); let any_holder = match holder { h @ MessageHolder { message: BusTrackMessage::SetMidiTrack(..), .. } => AnyMessageHolder::EmptyOutput(h), h @ MessageHolder { message: BusTrackMessage::AdjustRelease(..), .. } => AnyMessageHolder::EmptyOutput(h), h @ MessageHolder { message: BusTrackMessage::AdjustVolume(..), .. } => AnyMessageHolder::EmptyOutput(h), h @ MessageHolder { message: BusTrackMessage::GetRelease(..), .. } => AnyMessageHolder::F64Output(h), h @ MessageHolder { message: BusTrackMessage::GetVolume(..), .. } => AnyMessageHolder::F64Output(h), }; self.sender.send(any_holder).unwrap(); receiver_back.recv().unwrap() } }
调用示例
let track = BusTrack { midi_track: None, release: 0.5, volume: 1.0 }; let mailman = MailMan::new(track); // 无返回值命令 mailman.send_and_await_recv(BusTrackMessage::AdjustRelease(1.0, PhantomData)); // 有返回值命令,编译器推断返回f64 let release = mailman.send_and_await_recv(BusTrackMessage::GetRelease((), PhantomData));
内容的提问来源于stack exchange,提问作者DJosh

