Cross-platform abstract design
⚓ Rust 📅 2025-08-02 👤 surdeus 👁️ 11I aim to implement platform abstraction where callers use a unified interface without platform-specific logic. My initial approach returned platform-specific types directly via conditional compilation:
// -- snap --
pub trait BtDevice {
fn disconnect(&self) -> Result<(), Box<dyn std::error::Error>>;
}
#[derive(Default)]
pub struct WindowsBtDevice;
#[derive(Default)]
pub struct LinuxBtDevice;
#[derive(Default)]
pub struct MacOSBtDevice;
#[derive(Default)]
pub struct UnknownBtDevice;
pub struct Device;
impl Device {
#[cfg(target_os = "windows")]
pub fn new() -> WindowsBtDevice {
WindowsBtDevice::default()
}
#[cfg(target_os = "linux")]
pub fn new() -> LinuxBtDevice {
LinuxBtDevice::default()
}
#[cfg(target_os = "macos")]
pub fn new() -> MacOSBtDevice {
MacOSBtDevice::default()
}
#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
pub fn new() -> UnkonwnBtDevice {
UnknownBtDevice::default()
}
}
impl BtDevice for WindowsBtDevice {
fn disconnect(&self) -> Result<(), Box<dyn std::error::Error>> {
// do_something...
}
}
Copilot Reviewer recommended refactoring to return a unified Deviceenum encapsulating all platforms:
// -- snap --
pub trait BtDevice {
fn disconnect(&self) -> Result<(), Box<dyn std::error::Error>>;
}
#[derive(Default)]
pub struct WindowsBtDevice;
#[derive(Default)]
pub struct LinuxBtDevice;
#[derive(Default)]
pub struct MacOSBtDevice;
#[derive(Default)]
pub struct UnknownBtDevice;
pub enum Device {
Windows(WindowsBtDevice),
Linux(LinuxBtDevice),
MacOS(MacOSBtDevice),
Unknown(UnknownBtDevice),
}
impl Device {
pub fn new() -> Self {
#[cfg(target_os = "windows")]
{
Device::Windows(WindowsBtDevice::default())
}
#[cfg(target_os = "linux")]
{
Device::Linux(LinuxBtDevice::default())
}
#[cfg(target_os = "macos")]
{
Device::MacOS(MacOSBtDevice::default())
}
#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
{
Device::Unknown(UnknownBtDevice::default())
}
}
}
impl BtDevice for Device {
fn disconnect(&self) -> Result<(), Box<dyn std::error::Error>> {
match self {
// i don't like this :<
Device::Windows(device) => device.disconnect(),
Device::Linux(device) => todo!(),
Device::MacOS(device) => todo!(),
Device::Unknown(device) => todo!(),
}
}
}
impl BtDevice for WindowsBtDevice {
fn disconnect(&self) -> Result<(), Box<dyn std::error::Error>> {
// do_something...
}
}
My concern: The match in BtDevice for Device requires boilerplate delegation for every platform variant. Is this enum-based design truly beneficial compared to the initial approach?
2 posts - 2 participants
🏷️ Rust_feed