【问题标题】:in Rust no_std, how can I return one of multiple closures implementing a trait using stable rust?在 Rust no_std 中,如何返回使用稳定 rust 实现特征的多个闭包之一?
【发布时间】:2022-01-17 21:28:15
【问题描述】:

我正在使用 Rust 开发 Teensy 4.0 (--target thumbv7em-none-eabihf),这意味着我必须使用 #![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 板条箱 (Is it possible to use Box with 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.

按照 lkolbly 的建议,尝试使用 alloc-cortex-m 板条箱将 CortexMHeap 用作 #[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))]
  |                                         ^^^^^^^^^^^^^^^^^^^^^^^

如何使用 stable rust 在 no_std 环境中处理 dyn 特征实例?

【问题讨论】:

标签: rust embedded teensy


【解决方案1】:

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

这将需要在 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']
    );
}

Playground

【讨论】:

    【解决方案2】:

    这是一个全局分配器的示例,改编自 alloc-cortex-m,没有使用需要夜间 Rust 的 linked_list_allocatorconst_mut_refs 功能。

    注意:代码未经测试。

    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.
    

    allocator.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)
            });
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-07-07
      • 2016-05-29
      • 1970-01-01
      • 2022-08-15
      • 1970-01-01
      • 2016-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多