如何在不使用std?
的情况下实现以下示例let text = format!("example {:.1} test {:x} words {}", num1, num2, num3);
text
类型为&str
,num1
,num2
和num3
具有任何数字类型。
我尝试使用numtoa
和itoa/dtoa
来显示数字,但numtoa
不支持浮点数而itoa
不支持no_std
。我觉得在字符串中显示一个数字是相当普遍的,而且我可能遗漏了一些明显的东西。
答案 0 :(得分:10)
除Shepmaster's answer外,您还可以在没有分配器的情况下格式化字符串。
在core::fmt::Write
中,您只需要实施write_str
,然后免费获得write_fmt
。
使用format_args!(...)
(与format!
语法相同),您可以准备core::fmt::Arguments
值,该值可以传递给core::fmt::write
。
请参阅Playground:
#![crate_type = "dylib"]
#![no_std]
pub mod write_to {
use core::cmp::min;
use core::fmt;
pub struct WriteTo<'a> {
buffer: &'a mut [u8],
// on write error (i.e. not enough space in buffer) this grows beyond
// `buffer.len()`.
used: usize,
}
impl<'a> WriteTo<'a> {
pub fn new(buffer: &'a mut [u8]) -> Self {
WriteTo { buffer, used: 0 }
}
pub fn as_str(self) -> Option<&'a str> {
if self.used <= self.buffer.len() {
// only successful concats of str - must be a valid str.
use core::str::from_utf8_unchecked;
Some(unsafe { from_utf8_unchecked(&self.buffer[..self.used]) })
} else {
None
}
}
}
impl<'a> fmt::Write for WriteTo<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
if self.used > self.buffer.len() {
return Err(fmt::Error);
}
let remaining_buf = &mut self.buffer[self.used..];
let raw_s = s.as_bytes();
let write_num = min(raw_s.len(), remaining_buf.len());
remaining_buf[..write_num].copy_from_slice(&raw_s[..write_num]);
self.used += raw_s.len();
if write_num < raw_s.len() {
Err(fmt::Error)
} else {
Ok(())
}
}
}
pub fn show<'a>(buffer: &'a mut [u8], args: fmt::Arguments) -> Result<&'a str, fmt::Error> {
let mut w = WriteTo::new(buffer);
fmt::write(&mut w, args)?;
w.as_str().ok_or(fmt::Error)
}
}
pub fn test() {
let mut buf = [0u8; 64];
let _s: &str = write_to::show(
&mut buf,
format_args!("write some stuff {:?}: {}", "foo", 42),
).unwrap();
}
答案 1 :(得分:6)
一般情况下,不。 format!
分配String
,而no_std
环境没有分配器。
如果您有分配器,则可以使用alloc crate。此包含format!
宏。
这个箱子不稳定,所以你需要每晚使用Rust:
#![feature(alloc)]
#![crate_type = "dylib"]
#![no_std]
#[macro_use]
extern crate alloc;
fn thing() {
let text = format!("example {:.1} test {:x} words {}", 1, 2, 3);
}
另见:
答案 2 :(得分:0)
您还可以结合使用numtoa
和arrayvec
板条箱。示例:
#![no_std]
use numtoa::NumToA;
use arrayvec::ArrayString;
fn main() -> ! {
let mut num_buffer = [0u8; 20];
let mut text = ArrayString::<[_; 100]>::new();
let num1 = 123;
let num2 = 456;
let num3 = 789;
// text.clear(); (on subsequent usages)
text.push_str("example ");
text.push_str(num1.numtoa_str(10, &mut num_buffer));
text.push_str(" test ");
text.push_str(num2.numtoa_str(10, &mut num_buffer));
text.push_str(" words ");
text.push_str(num3.numtoa_str(10, &mut num_buffer));
}
请注意,push_str可能会惊慌。查看try_
-方法的API
和Cargo.toml
[dependencies]
arrayvec = { version = "0.5", default-features = false }
numtoa = "0.2"
答案 3 :(得分:0)
写一个格式化程序!
use core::fmt::{self, Write};
use core::str;
fn main() {
// For LCD 160 / 8 = 20 chars
let mut buf = [0u8; 20];
let mut buf = ByteMutWriter::new(&mut buf[..]);
buf.clear();
write!(&mut buf, "Hello {}!", "Rust").unwrap();
// buf.as_str()
}
pub struct ByteMutWriter<'a> {
buf: &'a mut [u8],
cursor: usize,
}
impl<'a> ByteMutWriter<'a> {
pub fn new(buf: &'a mut [u8]) -> Self {
ByteMutWriter { buf, cursor: 0 }
}
pub fn as_str(&self) -> &str {
str::from_utf8(&self.buf[0..self.cursor]).unwrap()
}
#[inline]
pub fn capacity(&self) -> usize {
self.buf.len()
}
pub fn clear(&mut self) {
self.cursor = 0;
}
pub fn len(&self) -> usize {
self.cursor
}
pub fn empty(&self) -> bool {
self.cursor == 0
}
pub fn full(&self) -> bool {
self.capacity() == self.cursor
}
}
impl fmt::Write for ByteMutWriter<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let cap = self.capacity();
for (i, &b) in self.buf[self.cursor..cap]
.iter_mut()
.zip(s.as_bytes().iter())
{
*i = b;
}
self.cursor = usize::min(cap, self.cursor + s.as_bytes().len());
Ok(())
}
}