join vector of non-strings into a string rust - rust

I have a type T that implements display (so it has a .to_string() method)
let my_vec = vec![T(), T(), T()];
println!("{}", my_vec.join(", "));
sadly errors with "trait bounds not satisfied" because the separator, ", ", is not of the same type as the vector's items (I'm pretty sure).
I guess my workaround is then
println!("{}", my_vec.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", "));
But isn't there anything shorter and clearer that I can write out instead of this?
I've just written this function to help me out:
fn join<T, I>(vec: &[T], sep: I) -> String
where
T: std::fmt::Display,
I: std::fmt::Display,
{
vec.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(&sep.to_string())
}
But I'd rather not have to. There must be alternative solutions that: are already built-in, don't require manual implementation, at least don't require the creation of a top-level function that's then only called twice.

AFAIK, your work around is good and sound without non-std libs.
Several cargos provide some helper functions for this issue, for example:
Playground
itertools and joinery
use itertools::Itertools;
use joinery::Joinable;
struct Color {
red: u8,
green: u8,
blue: u8,
}
impl std::fmt::Display for Color {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"RGB ({}, {}, {}) 0x{:02X}{:02X}{:02X}",
self.red, self.green, self.blue, self.red, self.green, self.blue
)
}
}
fn main() {
let colors = vec![
Color {
red: 128,
green: 255,
blue: 90,
},
Color {
red: 0,
green: 3,
blue: 254,
},
];
println!(
"{}",
colors
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(", ")
);
println!("{}", colors.iter().join(", "));
println!("{}", colors.join_with(", "));
}

Related

Rust struct field str slice with size

I can do the following:
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
struct Test {
field: [u8; 8],
}
But I want to format it as a string when I'm using the debug formatter, and I'm wondering if I could define the field as a str slice with a known size. I can't use String because I am loading the struct from memory, where it is not instantiated by my Rust program. I have read that string slices are represented using UTF-8 characters, but I need ASCII. Should I manually implement the Debug trait instead?
There's no automatic way of doing that, so you'll have to manually implement Debug. Be careful, however, that not every [u8; 8] is valid UTF-8 (unless you're actually guaranteed to get ASCII).
To be safe, you could switch whether to print field as a string or as an array of bytes based on whether it's legal UTF-8:
use std::fmt;
impl fmt::Debug for Test {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let utf8;
let value: &dyn fmt::Debug = if let Ok(s) = std::str::from_utf8(&self.field) {
utf8 = s;
&utf8
} else {
&self.field
};
f.debug_struct("Test")
.field("field", value)
.finish()
}
}
fn main() {
let valid_ascii = Test {
field: [b'a'; 8],
};
let invalid_ascii = Test {
field: [0xFF; 8],
};
println!("{:?}", valid_ascii); // Test { field: "aaaaaaaa" }
println!("{:?}", invalid_ascii); // Test { field: [255, 255, 255, 255, 255, 255, 255, 255] }
}
If you're guaranteed valid ASCII, you can of course just use std::str::from_utf8_unchecked and skip that step.

Rendering text in a separate function using piston2d in rust

I am trying to render text in a separate function using piston2d / piston_window. I am able to draw text just fine, but I can't figure out how to pass the appropriate parameters into a separate function.
I have studied What is GlyphCache type in a function to render text in Piston2d and adjusted my code accordingly, but I can't make sense of the error I am getting.
use piston_window::*;
fn main() {
let font = include_bytes!("IBMPlexSans-Regular.ttf");
let opengl = OpenGL::V3_2;
let settings = WindowSettings::new("test", [500, 500])
.graphics_api(opengl)
.fullscreen(false)
.vsync(true)
.exit_on_esc(true);
let mut window: PistonWindow = settings.build().unwrap();
let mut glyphs = Glyphs::from_bytes(
font,
window.create_texture_context(),
TextureSettings::new(),
)
.unwrap();
while let Some(e) = window.next() {
window.draw_2d(&e, |c, gfx, device| {
clear([0.2; 4], gfx);
text::Text::new_color([1.0, 1.0, 1.0, 0.7], 30)
.draw(
"Hi!",
&mut glyphs,
&c.draw_state,
c.transform
.trans(100., 100.),
gfx,
)
.unwrap();
glyphs.factory.encoder.flush(device);
});
}
}
fn render_text(
x: f64,
y: f64,
text: &str,
size: u32,
c: Context,
g: &mut G2d,
glyphs: &mut glyph_cache::rusttype::GlyphCache<GfxFactory, G2dTexture>,
) {
text::Text::new(size)
.draw(text, glyphs, &c.draw_state, c.transform.trans(x, y), g)
.unwrap();
}
I am receiving the following error:
error[E0277]: the trait bound `Texture<gfx_device_gl::Resources>: UpdateTexture<gfx_device_gl::factory::Factory>` is not satisfied
--> src/main.rs:54:10
|
54 | .draw(text, glyphs, &c.draw_state, c.transform.trans(x, y), g)
| ^^^^ the trait `UpdateTexture<gfx_device_gl::factory::Factory>` is not implemented for `Texture<gfx_device_gl::Resources>`
|
= help: the following implementations were found:
<Texture<R> as UpdateTexture<TextureContext<F, R, C>>>
= note: required because of the requirements on the impl of `CharacterCache` for `GlyphCache<'_, gfx_device_gl::factory::Factory, Texture<gfx_device_gl::Resources>>`
I am aware this is probably very piston-specific, but I would be very happy about any pointers.
Just had the same problem. It's almost a year after but documentation for piston_window isn't the best so maybe others will need it.
This worked for me
use piston_window::types::Color;
use piston_window::{text, Context, G2d, Glyphs, Transformed};
pub const text_color: Color = [1.0, 1.0, 1.0, 1.0];
pub fn draw_text(
ctx: &Context,
graphics: &mut G2d,
glyphs: &mut Glyphs,
color: Color,
pos: Position,
text: &str,
) {
text::Text::new_color(color, 20)
.draw(
text,
glyphs,
&ctx.draw_state,
ctx.transform.trans(pos.x as f64, pos.y as f64),
graphics,
)
.unwrap();
}
pub struct Pos {
pub x: f64,
pub y: f64,
}
pub fn main() {
let assets = find_folder::Search::ParentsThenKids(3, 3)
.for_folder("assets")
.unwrap();
let ref font = assets.join("retro-gaming.ttf");
let mut glyphs = window.load_font(font).unwrap();
let size = [500., 500.];
let mut window: PistonWindow = WindowSettings::new("Test", size)
.resizable(false)
.exit_on_esc(true)
.build()
.unwrap();
while let Some(event) = window.next() {
window.draw_2d(&event, |ctx, g, _| {
draw_text(&ctx, g, &mut glyphs, text_color, Pos { x: 0, y: 10 }, "20")
}
}
}
Note that I'm using different "Glyphs" than you are. There's additionally one more dependency in "Cargo.toml" namely find_folder = "0.3.0".
I'm not entirely sure whether the snippet above compiles and works. It is a quick refactor from this commit https://github.com/laszukdawid/rsnake/commit/e6e23563ebdd9c7b972ee17b5d0299e2202358cf.

get_value returning `f64` instead of `[u8; 4]`

I'm using the noise crate and having trouble understanding how to convert their Color type to an RGB value.
noise = "0.7.0"
pub type Color = [u8; 4];
I'm trying to use the get_value() function, seen here in the docs as:
pub fn get_value(&self, x: usize, y: usize) -> Color {
let (width, height) = self.size;
if x < width && y < height {
self.map[x + y * width]
} else {
self.border_color
}
}
get_value() is implemented for PlaneMapBuilder. So I would expect PlaneMapBuilder::get_value(x,y) to return something of the format [r,g,b,a], but this does not happen:
extern crate noise;
use noise::{utils::*, Billow};
fn main() {
let mut my_noise = PlaneMapBuilder::new(&Billow::new()).build();
let my_val = my_noise.get_value(1,1);
println!("{}", my_val.to_string());
///returns something like -0.610765515150546, not a [u8;4] as I would expect
}
In the docs I see this definition of add_gradient_point() which takes a Color as a parameter:
pub fn add_gradient_point(mut self, pos: f64, color: Color) -> Self {
// check to see if the vector already contains the input point.
if !self
.gradient_points
.iter()
.any(|&x| (x.pos - pos).abs() < std::f64::EPSILON)
{
// it doesn't, so find the correct position to insert the new
// control point.
let insertion_point = self.find_insertion_point(pos);
// add the new control point at the correct position.
self.gradient_points
.insert(insertion_point, GradientPoint { pos, color });
}
self
}
Here they use the [u8; 4] structure I would expect for the Color type:
let jade_gradient = ColorGradient::new()
.clear_gradient()
.add_gradient_point(-1.000, [24, 146, 102, 255])
.add_gradient_point(0.000, [78, 154, 115, 255])
What could explain this behavior?
get_value() is implemented for PlaneMapBuilder
You are correct that PlaneMapBuilder "implements" get_value(). However, it is not get_value() from NoiseImage. It is actually NoiseMap, where its get_value() returns a f64 and not Color.
Depending on what kind of "colors" you'd want, then you could instead use ImageRenderer and call its render() method with &my_noise, which returns a NoiseImage.
// noise = "0.7.0"
use noise::{utils::*, Billow};
fn main() {
let my_noise = PlaneMapBuilder::new(&Billow::new()).build();
let image = ImageRenderer::new().render(&my_noise);
let my_val = image.get_value(1, 1);
println!("{:?}", my_val);
// Prints: `[18, 18, 18, 255]`
}
Here they use the [u8; 4] structure I would expect for the Color type
Just to be clear, those are the same thing in this case. In short the type keyword allows you to define new "type aliases" for an existing types. Essentially, you'd be able to give a complex type a shorthand name. However, they are still the same type.

Can I perform binary tree search with the standard library without wrapping the float type and abusing the BTreeMap?

I would like to find the first element which is greater than a limit from an ordered collection. While iteration over it is always an option, I need a faster one. Currently, I came up with a solution like this but it feels a little hacky:
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::ops::Bound::{Included, Unbounded};
#[derive(Debug)]
struct FloatWrapper(f32);
impl Eq for FloatWrapper {}
impl PartialEq for FloatWrapper {
fn eq(&self, other: &Self) -> bool {
(self.0 - other.0).abs() < 1.17549435e-36f32
}
}
impl Ord for FloatWrapper {
fn cmp(&self, other: &Self) -> Ordering {
if (self.0 - other.0).abs() < 1.17549435e-36f32 {
Ordering::Equal
} else if self.0 - other.0 > 0.0 {
Ordering::Greater
} else if self.0 - other.0 < 0.0 {
Ordering::Less
} else {
Ordering::Equal
}
}
}
impl PartialOrd for FloatWrapper {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
The wrapper around the float is not nice even that I am sure that there will be no NaNs
The Range is also unnecessary since I want a single element.
Is there a better way of achieving a similar result using only Rust's standard library? I know that there are plenty of tree implementations but it feels like overkill.
After the suggestions in the answer to use the iterator I did a little benchmark with the following code:
fn main() {
let measure = vec![
10, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190,
200,
];
let mut measured_binary = Vec::new();
let mut measured_iter = Vec::new();
let mut measured_vec = Vec::new();
for size in measure {
let mut ww = BTreeMap::new();
let mut what_found = Vec::new();
for _ in 0..size {
let now: f32 = thread_rng().gen_range(0.0, 1.0);
ww.insert(FloatWrapper(now), now);
}
let what_to_search: Vec<FloatWrapper> = (0..10000)
.map(|_| thread_rng().gen_range(0.0, 0.8))
.map(|x| FloatWrapper(x))
.collect();
let mut rez = 0;
for current in &what_to_search {
let now = Instant::now();
let m = find_one(&ww, current);
rez += now.elapsed().as_nanos();
what_found.push(m);
}
measured_binary.push(rez);
rez = 0;
for current in &what_to_search {
let now = Instant::now();
let m = find_two(&ww, current);
rez += now.elapsed().as_nanos();
what_found.push(m);
}
measured_iter.push(rez);
let ww_in_vec: Vec<(FloatWrapper, f32)> =
ww.iter().map(|(&key, &value)| (key, value)).collect();
rez = 0;
for current in &what_to_search {
let now = Instant::now();
let m = find_three(&ww_in_vec, current);
rez += now.elapsed().as_nanos();
what_found.push(m);
}
measured_vec.push(rez);
println!("{:?}", what_found);
}
println!("binary :{:?}", measured_binary);
println!("iter_map :{:?}", measured_iter);
println!("iter_vec :{:?}", measured_vec);
}
fn find_one(from_what: &BTreeMap<FloatWrapper, f32>, what: &FloatWrapper) -> f32 {
let v: Vec<f32> = from_what
.range((Included(what), (Unbounded)))
.take(1)
.map(|(_, &v)| v)
.collect();
*v.get(0).expect("we are in truble")
}
fn find_two(from_what: &BTreeMap<FloatWrapper, f32>, what: &FloatWrapper) -> f32 {
from_what
.iter()
.skip_while(|(i, _)| *i < what) // Skipping all elements before it
.take(1) // Reducing the iterator to 1 element
.map(|(_, &v)| v) // Getting its value, dereferenced
.next()
.expect("we are in truble") // Our
}
fn find_three(from_what: &Vec<(FloatWrapper, f32)>, what: &FloatWrapper) -> f32 {
*from_what
.iter()
.skip_while(|(i, _)| i < what) // Skipping all elements before it
.take(1) // Reducing the iterator to 1 element
.map(|(_, v)| v) // Getting its value, dereferenced
.next()
.expect("we are in truble") // Our
}
The key takeaway for me is that it is worth to use the binary search after ~50 elements. In my case with 30000 elements means 200x speedup (at least based on this microbenchmark).
You said you wanted a std-only solution, but this is a common enough problem, so here's a solution using the crate ordered-float:
Cargo.toml
[dependencies]
ordered-float = "1.0"
main.rs
use ordered_float::OrderedFloat; // 1.0.2
use std::collections::BTreeMap;
fn main() {
let mut ww = BTreeMap::new();
ww.insert(OrderedFloat(1.0), "one");
ww.insert(OrderedFloat(2.0), "two");
ww.insert(OrderedFloat(3.0), "three");
ww.insert(OrderedFloat(4.0), "three");
let rez = ww.range(OrderedFloat(1.5)..).next().map(|(_, &v)| v);
println!("{:?}", rez);
}
prints
Some("two")
Now, isn't that nice and clean? If you want a less verbose syntax, I suggest wrapping the BTreeMap itself, so you can give it appropriately named methods that make sense for your application.
NaN behavior
Be aware that OrderedFloat may not behave the way you expect in the presence of NaNs:
NaN is sorted as greater than all other values and equal to itself, in contradiction with the IEEE standard.
Now that we've gone over and clarified the requirements a bit, there's a couple of bad news for you:
You're not getting away from the requirement to have a wrapping type. As I'm sure you've discovered, this is because no floating-point type implements Ord
You're also not getting away from a combinator of some sort
First, we're going to clear up your impl, as they both have shortfalls described in the comments. In the future, it may make sense to use the wrapper traits in eq-float, as they already implement all this. The implementations at fault are PartialEq and Ord, and they both break down on a few points. The new implementations:
impl Ord for FloatWrapper {
fn cmp(&self, other: &Self) -> Ordering {
self.0.partial_cmp(&other.0).unwrap_or_else(|| {
if self.0.is_nan() && !other.0.is_nan() {
Ordering::Less
} else if !self.0.is_nan() && other.0.is_nan() {
Ordering::Greater
} else {
Ordering::Equal
}
})
}
}
impl PartialEq for FloatWrapper {
fn eq(&self, other: &Self) -> bool {
if self.0.is_nan() && other.0.is_nan() {
true
} else {
self.0 == other.0
}
}
}
Nothing surprising, we're just abusing the fact that f32 implements PartialOrd for Ord and surfacing all other implementations on FloatWrapper itself.
Now, for the combinator. Your current combinator will force a range of elements to be stored temporarily in memory, to then discard one. We can do better by abusing the fact that iter() is a sorted iterator. So, we can skip while we search, and then take the first:
let mut first_element = ww.iter()
.skip_while(|(i, _)| *i < &FloatWrapper::new(1.5)) // Skipping all elements before it
.take(1) // Reducing the iterator to 1 element
.map(|(_, &v)| v) // Getting its value, dereferenced
.next(); // Our result
This yields a 10% speedup in low-element-count situations over your first implementation.

Can I format debug output as binary when the values are in a vector?

In Rust you can format numbers in different bases, which is really useful for bit twiddling:
println!("{:?} {:b} {:x}", 42, 42, 42); // 42 101010 2a
Ideally this would also work for vectors! While it works for hex:
println!("{:#x?}", vec![42, 43, 44]); // [ 0x2a, 0x2b, 0x2c ]
It does not work for binary:
println!("{:b}", vec![42, 43, 44]); // I wish this were [101010, 101011, 101100]
Instead giving:
the trait bound std::vec::Vec<{integer}>: std::fmt::Binary is not satisfied
Is there a way of doing binary formatting inside vectors?
Well a direct way, no, but I would do something like this:
use std::fmt;
struct V(Vec<u32>);
// custom output
impl fmt::Binary for V {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// extract the value using tuple idexing
// and create reference to 'vec'
let vec = &self.0;
// #count -> the index of the value,
// #n -> the value
for (count, n) in vec.iter().enumerate() {
if count != 0 { write!(f, " ")?; }
write!(f, "{:b}", n)?;
}
Ok(())
}
}
fn main() {
println!("v = {:b} ", V( vec![42, 43, 44] ));
}
Output:
$ rustc v.rs && ./v
v = 101010 101011 101100
I'm using rustc 1.31.1 (b6c32da9b 2018-12-18)
Rust fmt::binary reference.
Rust fmt::Display reference.

Resources