在 Rust no_std 中,如何使用稳定的 rust 返回实现某个特征的多个闭包之一?

问题描述 投票:0回答:2

我正在 Teensy 4.0 (

--target thumbv7em-none-eabihf
) 上使用 Rust,这意味着我必须使用
#![no_std]
.

在某些情况下,我想根据旋转开关的位置执行不同的操作。

下面是一个玩具示例,说明了一个问题,我想返回一系列实现特征的对象中的一个。

fn text_for(selector: i32) -> impl Fn()->Box<dyn Iterator<Item=char>> {
    match selector {
        1 => || {
            let rval : Box<dyn Iterator<Item=char>> = Box::new("author".chars());
            rval
        },
        _ => || {
            let rval : Box::<dyn Iterator<Item=char>> = Box::new(b"baseline".iter().map(|&b| (b) as char));
            rval
        }
    }
}

不幸的是

Box
在no_std环境中不可用。 我看到了对
alloc
板条箱的引用(是否可以将 Box 与 no_std 一起使用?),但是当我使用
extern crate alloc; use alloc::boxed::Box;
时,编译器会抱怨

error: no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait.

error: `#[alloc_error_handler]` function required, but not found.

note: Use `#![feature(default_alloc_error_handler)]` for a default error handler.

尝试使用

alloc-cortex-m
板条箱来使用
CortexMHeap
作为 lkolbly 建议的
#[global_allocator]
会导致以下错误

error[E0554]: `#![feature]` may not be used on the stable release channel
 --> /home/thoth/.cargo/registry/src/github.com-1ecc6299db9ec823/linked_list_allocator-0.8.11/src/lib.rs:1:41
  |
1 | #![cfg_attr(feature = "const_mut_refs", feature(const_mut_refs))]
  |                                         ^^^^^^^^^^^^^^^^^^^^^^^

如何使用稳定的 rust 在 no_std 环境中处理特征的

dyn
实例?

rust embedded teensy
2个回答
2
投票

编辑:
alloc-cortex-m
已弃用,请使用
embedded-alloc

这是一个全局分配器的示例,改编自

alloc-cortex-m
,但没有使用
const_mut_refs
linked_list_allocator
功能(需要每晚 Rust)。

注意:代码未经测试。

Cargo.toml:

[dependencies]
linked_list_allocator = { version = "0.9", default-features = false, features = ["use_spin"] }

lib.rs:

#![no_std]

mod allocator;

extern crate alloc;
use alloc::boxed::Box;

#[global_allocator]
static ALLOCATOR: allocator::CortexMHeap = unsafe { allocator::CortexMHeap::empty() };


#[entry]
fn main() -> ! {
    // Initialize the allocator BEFORE you use it
    let start = cortex_m_rt::heap_start() as usize;
    let size = 1024; // in bytes
    unsafe { ALLOCATOR.init(start, size) }

    // ...
}

// Your code using `Box` here.

分配器.rs:

use core::alloc::{GlobalAlloc, Layout};
use core::cell::RefCell;
use core::ptr::{self, NonNull};
use core::mem::MaybeUninit;

use cortex_m::interrupt::Mutex;
use linked_list_allocator::Heap;

pub struct CortexMHeap {
    heap: Mutex<RefCell<MaybeUninit<Heap>>>,
}

impl CortexMHeap {
    /// Crate a new UNINITIALIZED heap allocator
    ///
    /// # Safety
    ///
    /// You must initialize this heap using the
    /// [`init`](struct.CortexMHeap.html#method.init) method before using the allocator.
    pub const unsafe fn empty() -> CortexMHeap {
        CortexMHeap {
            heap: Mutex::new(RefCell::new(MaybeUninit::uninit())),
        }
    }

    fn heap(&self, cs: &cortex_m::interrupt::CriticalSection) -> &mut Heap {
        let heap = &mut *self.heap.borrow(cs).borrow_mut();
        // SAFETY: `init()` initializes this, and it's guaranteed to be called by preconditions of `empty()`.
        unsafe { &mut *heap.as_mut_ptr() }
    }

    /// Initializes the heap
    ///
    /// This function must be called BEFORE you run any code that makes use of the
    /// allocator.
    ///
    /// `start_addr` is the address where the heap will be located.
    ///
    /// `size` is the size of the heap in bytes.
    ///
    /// Note that:
    ///
    /// - The heap grows "upwards", towards larger addresses. Thus `end_addr` must
    ///   be larger than `start_addr`
    ///
    /// - The size of the heap is `(end_addr as usize) - (start_addr as usize)`. The
    ///   allocator won't use the byte at `end_addr`.
    ///
    /// # Safety
    ///
    /// Obey these or Bad Stuff will happen.
    ///
    /// - This function must be called exactly ONCE.
    /// - `size > 0`
    pub unsafe fn init(&self, start_addr: usize, size: usize) {
        cortex_m::interrupt::free(|cs| {
            let heap = &mut *self.heap.borrow(cs).borrow_mut();
            *heap = MaybeUninit::new(Heap::empty());
            (*heap.as_mut_ptr()).init(start_addr, size);
        });
    }

    /// Returns an estimate of the amount of bytes in use.
    pub fn used(&self) -> usize {
        cortex_m::interrupt::free(|cs| self.heap(cs).used())
    }

    /// Returns an estimate of the amount of bytes available.
    pub fn free(&self) -> usize {
        cortex_m::interrupt::free(|cs| self.heap(cs).free())
    }
}

unsafe impl GlobalAlloc for CortexMHeap {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        cortex_m::interrupt::free(|cs| {
            self.heap(cs)
                .allocate_first_fit(layout)
                .ok()
                .map_or(ptr::null_mut(), |allocation| allocation.as_ptr())
        })
    }

    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        cortex_m::interrupt::free(|cs| {
            self.heap(cs)
                .deallocate(NonNull::new_unchecked(ptr), layout)
        });
    }
}

1
投票

另一个答案中显示的一个选项是通过提供分配器来允许使用盒子。但是,如果您不需要在代码中的其他任何地方进行分配,则没有必要仅仅为了特征对象而引入它们 - 您还可以通过借用堆栈分配的值来创建特征对象。

这需要更改 API,以便闭包不会返回迭代器,而是将其传递给用户提供的使用它的闭包。消费闭包可以迭代它并提取有用的数据。例如:

fn text_for<F, R>(selector: i32) -> impl Fn(F) -> R where F: Fn(&mut dyn Iterator<Item = char>) -> R, { move |consume| { let (mut it1, mut it2); let it: &mut dyn Iterator<Item = char> = match selector { 1 => { it1 = "author".chars(); &mut it1 } _ => { it2 = b"baseline".iter().map(|&b| b as char); &mut it2 } }; consume(it) } }
您可以像这样使用这个版本的

text_for()

fn main() { let with_text_iter = text_for(1); assert_eq!( // example consume function just collects chars into Vec with_text_iter(|it| it.collect::<Vec<_>>()), &['a', 'u', 't', 'h', 'o', 'r'] ); }

游乐场

© www.soinside.com 2019 - 2024. All rights reserved.