在物联网和嵌入式系统蓬勃发展的今天,Rust 作为一种系统级编程语言正变得越来越重要。它的零成本抽象、内存安全和并发性使其成为嵌入式开发的理想选择。今天,我们将通过几个小游戏开发中的常见任务来探索 Rust 在嵌入式领域的强大功能。

嵌入式系统的挑战

嵌入式系统通常面临以下挑战:

  • 内存资源有限
  • 处理能力受限
  • 功耗要求严格
  • 需要高可靠性和实时响应

Rust 的设计哲学恰好能很好地应对这些挑战。

任务一:高效的除法和取模运算

在游戏中,我们经常需要进行除法和取模运算,比如计算精灵在网格中的位置。在一个资源受限的环境中,能够同时获得商和余数会大大提高效率。

pub fn divmod(dividend: i16, divisor: i16) -> (i16, i16) {
    (dividend / divisor, dividend % divisor)
}

这是一个简单却非常实用的函数。通过一次计算同时返回商和余数,避免了重复计算。在嵌入式系统中,每一次 CPU 周期都很宝贵。

元组的力量

pub fn divmod(dividend: i16, divisor: i16) -> (i16, i16) {
    (dividend / divisor, dividend % divisor)
}

Rust 的元组(tuple)特性让我们可以轻松地返回多个值,而不需要定义专门的结构体。这对于嵌入式开发尤其有价值,因为它减少了内存分配和代码复杂度。

任务二:迭代器的魅力

在游戏开发中,我们经常需要处理序列数据。Rust 的迭代器提供了一种高效且惰性求值的方式来处理数据流。

pub fn evens<T>(iter: impl Iterator<Item = T>) -> impl Iterator<Item = T> {
    iter.step_by(2)
}

这个函数的作用是从给定的迭代器中选出偶数位置的元素。这看起来简单,但它展示了 Rust 迭代器的强大之处:

泛型和 trait bounds

pub fn evens<T>(iter: impl Iterator<Item = T>) -> impl Iterator<Item = T> {
    iter.step_by(2)
}

通过使用泛型和 trait bounds,这个函数可以处理任何类型的迭代器,无论是数字、字符串还是自定义对象。这种灵活性和性能的结合正是 Rust 的魅力所在。

惰性求值

Rust 的迭代器是惰性求值的,这意味着它们只在需要时才进行计算。在内存受限的嵌入式系统中,这种特性可以显著减少内存占用。

任务三:曼哈顿距离计算

在网格游戏中,计算两点间的距离是一项基本操作。欧几里得距离虽然直观,但在网格中更常用的是曼哈顿距离。

pub struct Position(pub i16, pub i16);
impl Position {
    pub fn manhattan(&self) -> i16 {
        self.0.abs() + self.1.abs()
    }
}

元组结构体

pub struct Position(pub i16, pub i16);

我们使用了元组结构体(tuple struct)来表示位置。这是一种轻量级的数据结构,非常适合嵌入式环境。pub 关键字使字段公开,方便直接访问。

方法实现

impl Position {
    pub fn manhattan(&self) -> i16 {
        self.0.abs() + self.1.abs()
    }
}

曼哈顿距离的计算非常简单:两个坐标轴差值的绝对值之和。在游戏 AI 中,这个函数可以用来估算移动成本或检测碰撞。

完整代码展示

// This stub file contains items which aren't used yet; feel free to remove this module attribute
// to enable stricter warnings.
#![allow(unused)]

pub fn divmod(dividend: i16, divisor: i16) -> (i16, i16) {
    (dividend / divisor, dividend % divisor)
}

pub fn evens<T>(iter: impl Iterator<Item = T>) -> impl Iterator<Item = T> {
    iter.step_by(2)
}

pub struct Position(pub i16, pub i16);
impl Position {
    pub fn manhattan(&self) -> i16 {
        self.0.abs() + self.1.abs()
    }
}

测试验证

通过测试用例我们可以看到各种方法的行为:

mod divmod {
    //! tests of divmod
    //!
    //! note that we're only testing positive quantities; no need to get into the mod/rem distinction

    use low_power_embedded_game::divmod;

    #[test]
    fn example() {
        assert_eq!(divmod(10, 3), (3, 1));
    }

    #[test]
    fn powerup() {
        assert_eq!(divmod(100, 3), (33, 1));
    }

    #[test]
    fn less() {
        assert_eq!(divmod(3, 10), (0, 3));
    }

    #[test]
    fn eq() {
        assert_eq!(divmod(3, 3), (1, 0));
    }

    #[test]
    fn multiple() {
        assert_eq!(divmod(9, 3), (3, 0));
    }
}

mod evens {
    use low_power_embedded_game::evens;

    #[test]
    fn simple_i32() {
        let out: Vec<i32> = evens(0..).take(5).collect();
        assert_eq!(out, &[0, 2, 4, 6, 8]);
    }

    #[test]
    fn reverse_i32() {
        let out: Vec<i32> = evens((0..=10).rev()).collect();
        assert_eq!(out, &[10, 8, 6, 4, 2, 0]);
    }

    #[test]
    fn offset_i32() {
        let out: Vec<i32> = evens(1..).take(5).collect();
        assert_eq!(out, &[1, 3, 5, 7, 9]);
    }

    #[test]
    fn strs() {
        let input = "You really must never be above joking.".split_whitespace();
        let expected: Vec<_> = "You must be joking.".split_whitespace().collect();
        let out: Vec<_> = evens(input).collect();
        assert_eq!(out, expected);
    }
}

mod manhattan {
    use low_power_embedded_game::Position;

    #[test]
    fn origin() {
        assert_eq!(Position(0, 0).manhattan(), 0);
    }

    #[test]
    fn q1_unit() {
        assert_eq!(Position(1, 1).manhattan(), 2);
    }

    #[test]
    fn q2_unit() {
        assert_eq!(Position(1, -1).manhattan(), 2);
    }

    #[test]
    fn q3_unit() {
        assert_eq!(Position(-1, -1).manhattan(), 2);
    }

    #[test]
    fn q4_unit() {
        assert_eq!(Position(-1, 1).manhattan(), 2);
    }

    #[test]
    fn relative_prime() {
        assert_eq!(Position(30, 70).manhattan(), 100);
    }
}

Rust 在嵌入式开发中的优势

1. 零成本抽象

pub fn evens<T>(iter: impl Iterator<Item = T>) -> impl Iterator<Item = T> {
    iter.step_by(2)
}

Rust 的迭代器在编译后通常会被优化成与手写循环一样高效的机器码。

2. 内存安全

Rust 在编译时就能防止空指针解引用、缓冲区溢出等常见错误,这对于需要高可靠性的嵌入式系统至关重要。

3. 无垃圾回收

与 Go 或 Java 等语言不同,Rust 不需要垃圾回收器,这对于实时性要求高的嵌入式系统非常重要。

4. 优秀的工具链

Rust 提供了出色的包管理器 Cargo,以及丰富的生态系统,大大简化了嵌入式开发的复杂性。

实际应用场景

这些技术在实际的嵌入式开发中有广泛应用:

  1. 游戏开发:处理游戏对象的位置、动画和碰撞检测
  2. IoT 设备:传感器数据处理和通信协议实现
  3. 机器人控制:路径规划和运动控制算法
  4. 汽车电子:ADAS 系统和车载娱乐系统
  5. 航空航天:飞行控制系统和卫星数据处理

总结

通过这个练习,我们看到了 Rust 在嵌入式开发中的巨大潜力:

  • 元组提供了简洁的多值返回机制
  • 迭代器实现了高效的数据处理流水线
  • 结构体元组结构体提供了灵活的数据建模方式
  • trait system支持了强大的泛型编程

Rust 不仅是一门系统编程语言,更是现代嵌入式开发的优秀选择。它能够在保证内存安全的同时,提供与 C/C++ 相媲美的性能。

随着 Rust 在嵌入式领域的生态不断完善,我们可以期待看到更多基于 Rust 的创新嵌入式产品出现。

在下一篇文章中,我们将继续探索 Rust 的更多强大功能!

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐