For example, I have a Vec<String> and an array storing indexes.
let src = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let idx_arr = [2_usize, 0, 1];
The indexes stored in idx_arr comes from the range 0..src.len(), without repetition or omission.
I want to move the elements in src to another container in the given order, until the vector is completely consumed. For example,
let iter = into_iter_in_order(src, &idx_arr);
for s in iter {
// s: String
}
// or
consume_vec_in_order(src, &idx_arr, |s| {
// s: String
});
If the type of src can be changed to Vec<Option<String>>, things will be much easier, just use src[i].take(). However, it cannot.
Edit:
"Another container" refers to any container, such as a queue or hash set. Reordering in place is not the answer to the problem. It introduces the extra time cost of O(n). The ideal method should be 0-cost.
Not sure if my algorithm satisfies your requirements but here I have an algorithm that can consume the provided vector in-order without initializing a new temporary vector, which is more efficient for a memory.
fn main() {
let src = &mut vec!["a".to_string(), "b".to_string(), "c".to_string(), "d".to_string()];
let idx_arr = [2_usize, 3, 1, 0];
consume_vector_in_order(src, idx_arr.to_vec());
println!("{:?}", src); // d , c , a , b
}
// In-place consume vector in order
fn consume_vector_in_order<T>(v: &mut Vec<T>, inds: Vec<usize>) -> &mut Vec<T>
where
T: Default,
{
let mut i: usize = 0;
let mut temp_inds = inds.to_vec();
while i < inds.to_vec().len() {
let s_index = temp_inds[i];
if s_index != i {
let new_index = temp_inds[s_index];
temp_inds.swap(s_index, new_index);
v.swap(s_index, new_index);
} else {
i += 1;
}
}
v
}
You can use the technique found in How to sort a Vec by indices? (using my answer in particular) since that can reorder the data in-place from the indices, and then its just simple iteration:
fn consume_vec_in_order<T>(mut vec: Vec<T>, order: &[usize], mut cb: impl FnMut(T)) {
sort_by_indices(&mut vec, order.to_owned());
for elem in vec {
cb(elem);
}
}
Full example available on the playground.
Edit:
An ideal method, but needs to access unstable features and functions not exposed by the standard library.
use std::alloc::{Allocator, RawVec};
use std::marker::PhantomData;
use std::mem::{self, ManuallyDrop};
use std::ptr::{self, NonNull};
#[inline]
unsafe fn into_iter_in_order<'a, T, A: Allocator>(
vec: Vec<T, A>,
order: &'a [usize],
) -> IntoIter<'a, T, A> {
unsafe {
let mut vec = ManuallyDrop::new(vec);
let cap = vec.capacity();
let alloc = ManuallyDrop::new(ptr::read(vec.allocator()));
let ptr = order.as_ptr();
let end = ptr.add(order.len());
IntoIter {
buf: NonNull::new_unchecked(vec.as_mut_ptr()),
_marker_1: PhantomData,
cap,
alloc,
ptr,
end,
_marker_2: PhantomData,
}
}
}
struct IntoIter<'a, T, A: Allocator> {
buf: NonNull<T>,
_marker_1: PhantomData<T>,
cap: usize,
alloc: ManuallyDrop<A>,
ptr: *const usize,
end: *const usize,
_marker_2: PhantomData<&'a usize>,
}
impl<T, A: Allocator> Iterator for IntoIter<T, A> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<T> {
if self.ptr == self.end {
None
} else {
let idx = unsafe { *self.ptr };
self.ptr = unsafe { self.ptr.add(1) };
if T::IS_ZST {
Some(unsafe { mem::zeroed() })
} else {
Some(unsafe { ptr::read(self.buf.as_ptr().add(idx)) })
}
}
}
}
impl<#[may_dangle] T, A: Allocator> Drop for IntoIter<T, A> {
fn drop(&mut self) {
struct DropGuard<'a, T, A: Allocator>(&'a mut IntoIter<T, A>);
impl<T, A: Allocator> Drop for DropGuard<'_, T, A> {
fn drop(&mut self) {
unsafe {
// `IntoIter::alloc` is not used anymore after this and will be dropped by RawVec
let alloc = ManuallyDrop::take(&mut self.0.alloc);
// RawVec handles deallocation
let _ = RawVec::from_raw_parts_in(self.0.buf.as_ptr(), self.0.cap, alloc);
}
}
}
let guard = DropGuard(self);
// destroy the remaining elements
unsafe {
while self.ptr != self.end {
let idx = *self.ptr;
self.ptr = self.ptr.add(1);
let p = if T::IS_ZST {
self.buf.as_ptr().wrapping_byte_add(idx)
} else {
self.buf.as_ptr().add(idx)
};
ptr::drop_in_place(p);
}
}
// now `guard` will be dropped and do the rest
}
}
Example:
let src = vec![
"0".to_string(),
"1".to_string(),
"2".to_string(),
"3".to_string(),
"4".to_string(),
];
let mut dst = vec![];
let iter = unsafe { into_iter_in_order(src, &[2, 1, 3, 0, 4]) };
for s in iter {
dst.push(s);
}
assert_eq!(dst, vec!["2", "1", "3", "0", "4"]);
My previous answer:
use std::mem;
use std::ptr;
pub unsafe fn consume_vec_in_order<T>(vec: Vec<T>, order: &[usize], mut cb: impl FnMut(T)) {
// Check whether `order` contains all numbers in 0..len without repetition
// or omission.
if cfg!(debug_assertions) {
use std::collections::HashSet;
let n = order.len();
if n != vec.len() {
panic!("The length of `order` is not equal to that of `vec`.");
}
let mut set = HashSet::<usize>::new();
for &idx in order {
if idx >= n {
panic!("`{idx}` in the `order` is out of range (0..{n}).");
} else if set.contains(&idx) {
panic!("`order` contains the repeated element `{idx}`");
} else {
set.insert(idx);
}
}
}
unsafe {
for &idx in order {
let s = ptr::read(vec.get_unchecked(idx));
cb(s);
}
vec.set_len(0);
}
}
Example:
let src = vec![
"0".to_string(),
"1".to_string(),
"2".to_string(),
"3".to_string(),
"4".to_string(),
];
let mut dst = vec![];
consume_vec_in_order(
src,
&[2, 1, 3, 0, 4],
|elem| dst.push(elem),
);
assert_eq!(dst, vec!["2", "1", "3", "0", "4"]);
I'm attempting to implement SHA3-512 the way it is done by NIST in FIPS PUB 202:
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf.
It works whenever I plug in the test vector's bit values + "01"(look for HERE in code), and for the blank value test case.
Example working test vector:
https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA3-512_Msg5.pdf
For some reason, whenever string values such as "abc" are plugged in, the hash function fails to work. Despite the conversion from string to binary string being fine, I believe the issue is with the padding function but I cant see where I messed up.
/* Keccak */
struct Keccak<'a> {
b: usize,
d: usize,
r: usize,
l: usize,
w: usize,
delim: &'a str,
}
impl Keccak<'_> {
pub fn new(digest: usize, bits: usize, delim: &str) -> Keccak {
Keccak {
b: bits,
d: digest,
r: (bits - digest * 2),
w: bits / 25,
l: (((bits / 25) as f32).log2()) as usize,
delim,
}
}
pub fn hash(&mut self, msg: &mut String) {
self.sponge(msg);
}
// SPONGE the provided string of binary m
fn sponge(&mut self, msg: &mut String) {
// Pad the message using pad10*1
// HERE: let mut msg = &mut String::from("01");
let len = msg.len() as isize;
self.pad101(msg, self.r as isize, len);
// Let n be size of P
let n = msg.len() / self.r;
// Create vector s of size b/8, each value is a byte
let mut s: Vec<String> = vec!["0".repeat(8); self.b / 8];
// Xor s and p for every p in P
let c = self.b - self.r;
for i in 0..n {
// let p be the message of len(r) + 0^c of size b
let p = format!("{}{}", &msg[i * self.r..(i + 1) * self.r], "0".repeat(c));
// Xor s and p in byte sized intervals, then do a round of keccakf
s = self.keccackf(
&(0..p.len())
.step_by(8)
.map(|x| {
format!(
"{:08?}",
p[x..x + 8].parse::<u32>().unwrap() ^ s[x / 8].parse::<u32>().unwrap()
)
})
.collect::<Vec<String>>()
.join(""),
);
let mut z = String::new();
while z.len() <= self.d {
let mut str = s.join("");
str.truncate(self.r);
z += &str;
s = self.keccackf(&(s.join("")));
}
z.truncate(self.d);
self.from_bin_string(&z);
}
}
// Convert string m to state array
fn to_state(&self, msg: &str) -> Vec<Vec<Vec<u8>>> {
let mut s: Vec<Vec<Vec<u8>>> = Vec::new();
for x in 0..5 {
let mut row = Vec::new();
for y in 0..5 {
let mut column = Vec::new();
for z in 0..self.w {
let i = self.w * (5 * y + x) + z;
column.push(msg[i..i + 1].parse::<u8>().unwrap());
}
row.push(column)
}
s.push(row);
}
s
}
// Convert state array to vector of string
fn from_state(&self, state: Vec<Vec<Vec<u8>>>) -> Vec<String> {
let mut result: Vec<String> = Vec::new();
let mut temp = String::new();
for i in 0..5 {
for j in 0..5 {
for w in 0..self.w {
temp += &format!("{}", state[j][i][w]);
if (temp.len() == 8) {
result.push(temp.clone());
temp = String::new();
}
}
}
}
result
}
fn theta(&self, state: &mut Vec<Vec<Vec<u8>>>) {
// C NIST
fn c(x: usize, z: usize, state: &Vec<Vec<Vec<u8>>>) -> u8 {
state[x][0][z] ^ state[x][1][z] ^ state[x][2][z] ^ state[x][3][z] ^ state[x][4][z]
}
// D NIST
fn d(x: usize, z: usize, state: &Vec<Vec<Vec<u8>>>, w: isize) -> u8 {
c(((x as isize) - 1).rem_euclid(5) as usize, z, state)
^ c(
(x + 1).rem_euclid(5),
((z as isize) - 1).rem_euclid(w) as usize,
state,
)
}
// Let s be a'
let mut s = vec![vec![vec![0; self.w]; 5]; 5];
// Save xor'd values into a'
for x in 0..5 {
for y in 0..5 {
for z in 0..self.w {
s[x][y][z] = state[x][y][z] ^ d(x, z, &state, self.w as isize);
}
}
}
s.clone_into(state);
}
fn rho(&self, state: &mut Vec<Vec<Vec<u8>>>) {
// Let s be a'
let mut s = vec![vec![vec![0; self.w]; 5]; 5];
// Set all z values of a' = a
state[0][0].clone_into(&mut s[0][0]);
// Let coords represent (x, y)
let mut coords = [1, 0];
for t in 0..24 {
for z in 0..self.w {
s[coords[0]][coords[1]][z] = state[coords[0]][coords[1]]
[((z as isize) - (t + 1) * (t + 2) / 2).rem_euclid(self.w as isize) as usize];
}
coords = [coords[1], (2 * coords[0] + 3 * coords[1]).rem_euclid(5)];
}
s.clone_into(state);
}
fn pi(&self, state: &mut Vec<Vec<Vec<u8>>>) {
let mut s: Vec<Vec<Vec<u8>>> = Vec::new();
for x in 0..5 {
let mut row = Vec::new();
for y in 0..5 {
let mut column = Vec::new();
for z in 0..self.w {
column.push(state[((x as usize) + 3 * y).rem_euclid(5)][x][z]);
}
row.push(column)
}
s.push(row);
}
s.clone_into(state)
}
fn chi(&self, state: &mut Vec<Vec<Vec<u8>>>) {
let mut s: Vec<Vec<Vec<u8>>> = Vec::new();
for x in 0..5 {
let mut row = Vec::new();
for y in 0..5 {
let mut column = Vec::new();
for z in 0..self.w {
column.push(
state[x][y][z]
^ ((state[(x + 1).rem_euclid(5)][y][z] ^ 1)
* state[(x + 2).rem_euclid(5)][y][z]),
);
}
row.push(column)
}
s.push(row);
}
s.clone_into(state)
}
fn rc(&self, t: usize) -> u8 {
if t.rem_euclid(255) == 0 {
return 1;
}
let mut r = std::collections::VecDeque::new();
r.extend(
"10000000"
.chars()
.map(|x| x.to_string().parse::<u8>().unwrap()),
);
for _ in 0..t.rem_euclid(255) {
r.push_front(0);
r[0] ^= r[8];
r[4] ^= r[8];
r[5] ^= r[8];
r[6] ^= r[8];
r.truncate(8);
}
return r[0];
}
fn iota(&self, state: &mut Vec<Vec<Vec<u8>>>, round: usize) {
let mut r: Vec<u8> = vec![0; self.w];
for j in 0..=self.l {
r[((2 as isize).pow(j as u32) - 1) as usize] = self.rc(j + 7 * round);
}
for z in 0..self.w {
state[0][0][z] ^= r[z];
}
}
fn keccackf(&self, msg: &str) -> Vec<String> {
let mut state = self.to_state(msg);
// Go through all the rounds
for r in 0..(12 + 2 * self.l) {
self.theta(&mut state);
self.rho(&mut state);
self.pi(&mut state);
self.chi(&mut state);
self.iota(&mut state, r);
}
let res = self.from_state(state);
res
}
// Pad10*1
fn pad101(&mut self, msg: &mut String, x: isize, m: isize) {
let j = (-m - 2).rem_euclid(x);
msg.push_str("1");
msg.push_str(&"0".repeat(j as usize));
msg.push('1');
}
// For nist test vectors
fn from_bin_string(&self, str: &str) {
let z = (0..str.len())
.step_by(8)
.map(|i| (&str[i..i + 8]).chars().rev().collect::<String>())
.collect::<Vec<String>>();
for i in 0..z.len() {
print!("{:02x}", u32::from_str_radix(&z[i], 2).unwrap());
}
}
}
/* Sha3 */
pub struct SHA3<'a> {
kec: Option<Keccak<'a>>,
}
impl SHA3<'_> {
// Creates new hash variable with default hash function, currently SHA512
pub fn new() -> SHA3<'static> {
SHA3 { kec: None }
}
// Sha512
fn SHA512(&mut self, mut msg: String) {
match self.kec.as_mut() {
// Provided kec hash msg
Some(kec) => kec.hash(&mut msg),
// Otherwise create new kec and call function again
None => {
// Creating new keccack struct, l & w & state are defined in NIST
self.kec = Some(Keccak::new(512, 1600, "0110"));
self.SHA512(msg)
}
}
}
// Return hashed vector
pub fn hash(&mut self, m: &str) {
self.SHA512(self.to_bin_string(m) + "01");
}
// Return hexdigest output
pub fn hexdigest(&self, m: &str) {}
// Convert string slice to binary string
fn to_bin_string(&self, str: &str) -> String {
String::from(str)
.as_bytes()
.into_iter()
.map(|x| format!("{:08b}", x))
.collect::<Vec<String>>()
.join("")
}
}
I'm building a game engine / CPU 3D renderer to learn the basics of rendering following a course.
I'm trying to organize the code so that things are encapsulated and I can provide a public API that's easy to use.
For this, I want to provide this engine.on_update(|&mut engine| {}) method so users can pass in their own code and use the engine in there.
This callback is then executed as part of the engine's update loop.
Here is what this looks like now (I trimmed the code to make this easier as its already quite long)
This is the engine module.
use std::time::{Duration, Instant};
use sdl2::{
event::Event,
keyboard::Keycode,
pixels::PixelFormatEnum,
render::{Canvas, Texture, UpdateTextureError},
video::Window,
EventPump,
};
use crate::buffer::{ClearAuto, ClearColor, ColorBuffer, Drawable};
use crate::utils::NumOption;
pub struct EngineConfigParams {
pub window_title: Option<String>,
pub width: Option<usize>,
pub height: Option<usize>,
pub clear_color: Option<u32>,
pub fps: Option<u32>,
}
impl Default for EngineConfigParams {
fn default() -> Self {
EngineConfigParams {
window_title: None,
width: None,
height: None,
clear_color: None,
fps: None,
}
}
}
pub struct EngineConfig {
window_title: String,
width: usize,
height: usize,
clear_color: u32,
fps: u32,
}
impl EngineConfig {
pub fn new(params: EngineConfigParams) -> Self {
let default = EngineConfig::default();
EngineConfig {
window_title: params.window_title.unwrap_or(default.window_title),
width: params.width.unwrap_gt_or(0, default.width),
height: params.height.unwrap_gt_or(0, default.height),
clear_color: params.clear_color.unwrap_or(default.clear_color),
fps: params.fps.unwrap_gt_or(0, default.fps),
}
}
pub fn window_title(&self) -> &String {
&self.window_title
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn clear_color(&self) -> u32 {
self.clear_color
}
pub fn fps(&self) -> u32 {
self.fps
}
}
impl Default for EngineConfig {
fn default() -> Self {
EngineConfig {
window_title: "3D renderer".to_string(),
width: 800,
height: 600,
clear_color: 0xFF000000,
fps: 60,
}
}
}
type EngineUpdateFn<'a> = &'a mut dyn FnMut(&mut EngineCore);
pub struct Engine<'a> {
core: EngineCore,
update: Option<EngineUpdateFn<'a>>,
previous_frame_time: Instant,
target_frame_time: Duration,
}
impl<'a> Engine<'a> {
pub fn build(mut config: EngineConfig) -> Engine<'a> {
let ctx = sdl2::init().unwrap();
let video = ctx.video().unwrap();
match video.display_mode(0, 0) {
Ok(mode) => {
config.width = mode.w as usize;
config.height = mode.h as usize;
println!("Display mode: {:?}", mode);
}
Err(e) => eprintln!(
"Failed to get display mode: {}, using default width and height",
e
),
};
let width = config.width;
let height = config.height;
let window = video
.window(&config.window_title, width as u32, height as u32)
.borderless()
.position_centered()
.fullscreen()
.build()
.unwrap();
let canvas = window.into_canvas().build().unwrap();
let color_buffer = ColorBuffer::new(width, height);
let event_pump = ctx.event_pump().unwrap();
println!("WindowCtx w: {} h: {}", width, height);
let fps = config.fps;
Engine {
core: EngineCore {
config,
canvas,
color_buffer,
event_pump,
},
update: None,
previous_frame_time: Instant::now(),
target_frame_time: Duration::new(0, 1_000_000_000u32 / fps),
}
}
pub fn config(&self) -> &EngineConfig {
&self.core.config
}
pub fn on_update(&mut self, f: EngineUpdateFn<'a>) {
self.update = Some(f);
self.update();
}
pub fn user_update(&mut self) {
self.update.as_mut().unwrap()(&mut self.core);
}
pub fn update(&mut self) {
self.target_frame_time = Duration::new(0, 1_000_000_000u32 / self.core.config.fps);
let texture_creator = self.core.canvas.texture_creator();
let mut texture = texture_creator
.create_texture(
PixelFormatEnum::ARGB8888,
sdl2::render::TextureAccess::Streaming,
self.core.config.width as u32,
self.core.config.height as u32,
)
.unwrap();
let mut running = true;
while running {
self.previous_frame_time = Instant::now();
running = self.core.process_input();
self.user_update();
self.core.render_buffer(&mut texture).unwrap();
self.core.clear();
let now = Instant::now();
let frame_time = now - self.previous_frame_time;
println!(
"Time this frame {}ms {} FPS",
frame_time.as_millis(),
1000u128 / frame_time.as_millis()
);
if frame_time.as_nanos() < self.target_frame_time.as_nanos() {
::std::thread::sleep(self.target_frame_time - frame_time);
}
}
}
}
pub struct EngineCore {
config: EngineConfig,
canvas: Canvas<Window>,
color_buffer: ColorBuffer,
event_pump: EventPump,
}
impl EngineCore {
fn process_input(&mut self) -> bool {
for event in self.event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => {
println!("Received quit event, shutting down");
return false;
}
_ => {}
}
}
true
}
fn render_buffer(&mut self, texture: &mut Texture) -> Result<(), UpdateTextureError> {
self.copy_buffer_to_canvas(texture)?;
self.canvas.present();
Ok(())
}
fn copy_buffer_to_canvas(&mut self, texture: &mut Texture) -> Result<(), UpdateTextureError> {
texture.update(None, self.color_buffer.pixels(), self.config.width * 4)?;
self.canvas.copy(texture, None, None).unwrap();
Ok(())
}
pub fn config(&self) -> &EngineConfig {
&self.config
}
}
impl ClearAuto for EngineCore {
fn clear(&mut self) {
self.color_buffer.clear(self.config.clear_color);
}
}
impl Drawable for EngineCore {
fn draw_grid(&mut self, spacing: usize, color: Option<u32>) {
self.color_buffer.draw_grid(spacing, color);
}
fn draw_rect(&mut self, x: usize, y: usize, width: usize, height: usize, color: u32) {
self.color_buffer.draw_rect(x, y, width, height, color);
}
fn draw_point(&mut self, x: usize, y: usize, color: u32) {
self.color_buffer.draw_point(x, y, color);
}
}
And in main.rs (Again trimmed of the mesh/points manipulations)
use renderer3d::prelude::*;
use vecx::Vec3;
pub fn main() {
let mut eng = Engine::build(EngineConfig {
window_title: "3d Renderer".to_string(),
width: 800,
height: 600,
clear_color: 0xFF000000,
});
let points = build_cube();
let fov = 640.0;
let cam_pos = Vec3(0.0, 0.0, -5.0);
let mut rotation = Vec3(0.0, 0.0, 0.0);
println!("Start update");
eng.on_update(&mut |eng: &mut Engine| {
eng.draw_grid(10, Some(0xFF333333));
rotation = rotation + Vec3(0.01, 0.02, 0.0);
let mut points = transform_points(&points, rotation);
points = project_points(&points, cam_pos, fov);
points.iter().for_each(|point| {
let mut x = point.x();
let mut y = point.y();
x += eng.config().width as f64 / 2.0;
y += eng.config().height as f64 / 2.0;
eng.draw_rect(x as usize, y as usize, 4, 4, 0xFFFF0000);
});
});
}
As you can see, it's important that this callback can access and mutate variables from the outer scope.
I've been through a lot of trial and error and I ended up finding something that works but the fact I have to do unsafe code makes me wonder if that's really the correct way to do this.
Especially I'm worried that I'm trying to design this too much as I would in other less safe/restrictive languages and that I'm missing how to design this properly in Rust.
Update
Here is the full final code after applying the accepted answer's solution
engine module
use std::time::{Duration, Instant};
use sdl2::{
event::Event,
keyboard::Keycode,
pixels::PixelFormatEnum,
render::{Canvas, Texture, UpdateTextureError},
video::Window,
EventPump,
};
use crate::buffer::{ClearAuto, ClearColor, ColorBuffer, Drawable};
use crate::utils::NumOption;
pub struct EngineConfigParams {
pub window_title: Option<String>,
pub width: Option<usize>,
pub height: Option<usize>,
pub clear_color: Option<u32>,
pub fps: Option<u32>,
}
impl Default for EngineConfigParams {
fn default() -> Self {
EngineConfigParams {
window_title: None,
width: None,
height: None,
clear_color: None,
fps: None,
}
}
}
pub struct EngineConfig {
window_title: String,
width: usize,
height: usize,
clear_color: u32,
fps: u32,
}
impl EngineConfig {
pub fn new(params: EngineConfigParams) -> Self {
let default = EngineConfig::default();
EngineConfig {
window_title: params.window_title.unwrap_or(default.window_title),
width: params.width.unwrap_gt_or(0, default.width),
height: params.height.unwrap_gt_or(0, default.height),
clear_color: params.clear_color.unwrap_or(default.clear_color),
fps: params.fps.unwrap_gt_or(0, default.fps),
}
}
pub fn window_title(&self) -> &String {
&self.window_title
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
}
impl Default for EngineConfig {
fn default() -> Self {
EngineConfig {
window_title: "3D renderer".to_string(),
width: 800,
height: 600,
clear_color: 0xFF000000,
fps: 60,
}
}
}
type EngineUpdateFn<'a> = &'a mut dyn FnMut(&mut EngineCore);
pub struct Engine<'a> {
core: EngineCore,
update: Option<EngineUpdateFn<'a>>,
previous_frame_time: Instant,
target_frame_time: Duration,
}
impl<'a> Engine<'a> {
pub fn build(mut config: EngineConfig) -> Engine<'a> {
let ctx = sdl2::init().unwrap();
let video = ctx.video().unwrap();
config = EngineConfig {
width: config.width,
height: config.height,
..EngineConfig::default()
};
match video.display_mode(0, 0) {
Ok(mode) => {
config.width = mode.w as usize;
config.height = mode.h as usize;
println!("Display mode: {:?}", mode);
}
Err(e) => eprintln!(
"Failed to get display mode: {}, using default width and height",
e
),
};
let width = config.width;
let height = config.height;
let window = video
.window(&config.window_title, width as u32, height as u32)
.borderless()
.position_centered()
.fullscreen()
.build()
.unwrap();
let canvas = window.into_canvas().build().unwrap();
let color_buffer = ColorBuffer::new(width, height);
let event_pump = ctx.event_pump().unwrap();
println!("WindowCtx w: {} h: {}", width, height);
let fps = config.fps;
Engine {
core: EngineCore {
config,
canvas,
color_buffer,
event_pump,
},
update: None,
previous_frame_time: Instant::now(),
target_frame_time: Duration::new(0, 1_000_000_000u32 / fps),
}
}
pub fn config(&self) -> &EngineConfig {
&self.core.config
}
pub fn on_update(&mut self, f: EngineUpdateFn<'a>) {
self.update = Some(f);
self.update();
}
pub fn user_update(&mut self) {
self.update.as_mut().unwrap()(&mut self.core);
}
pub fn update(&mut self) {
self.target_frame_time = Duration::new(0, 1_000_000_000u32 / self.core.config.fps);
let texture_creator = self.core.canvas.texture_creator();
let mut texture = texture_creator
.create_texture(
PixelFormatEnum::ARGB8888,
sdl2::render::TextureAccess::Streaming,
self.core.config.width as u32,
self.core.config.height as u32,
)
.unwrap();
let mut running = true;
while running {
self.previous_frame_time = Instant::now();
running = self.core.process_input();
self.user_update();
self.core.render_buffer(&mut texture).unwrap();
self.core.clear();
let now = Instant::now();
let frame_time = now - self.previous_frame_time;
println!(
"Time this frame {}ms {} FPS",
frame_time.as_millis(),
1000u128 / frame_time.as_millis()
);
if frame_time.as_nanos() < self.target_frame_time.as_nanos() {
::std::thread::sleep(self.target_frame_time - frame_time);
}
}
}
}
pub struct EngineCore {
config: EngineConfig,
canvas: Canvas<Window>,
color_buffer: ColorBuffer,
event_pump: EventPump,
}
impl EngineCore {
fn process_input(&mut self) -> bool {
for event in self.event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => {
println!("Received quit event, shutting down");
return false;
}
_ => {}
}
}
true
}
fn render_buffer(&mut self, texture: &mut Texture) -> Result<(), UpdateTextureError> {
self.copy_buffer_to_canvas(texture)?;
self.canvas.present();
Ok(())
}
fn copy_buffer_to_canvas(&mut self, texture: &mut Texture) -> Result<(), UpdateTextureError> {
texture.update(None, self.color_buffer.pixels(), self.config.width * 4)?;
self.canvas.copy(texture, None, None).unwrap();
Ok(())
}
pub fn config(&self) -> &EngineConfig {
&self.config
}
}
impl ClearAuto for EngineCore {
fn clear(&mut self) {
self.color_buffer.clear(self.config.clear_color);
}
}
impl Drawable for EngineCore {
fn draw_grid(&mut self, spacing: usize, color: Option<u32>) {
self.color_buffer.draw_grid(spacing, color);
}
fn draw_rect(&mut self, x: usize, y: usize, width: usize, height: usize, color: u32) {
self.color_buffer.draw_rect(x, y, width, height, color);
}
fn draw_point(&mut self, x: usize, y: usize, color: u32) {
self.color_buffer.draw_point(x, y, color);
}
}
main.rs
use renderer3d::{prelude::*, Mesh};
use vecx::Vec3;
pub fn main() {
let mut eng = Engine::build(EngineConfig::new(EngineConfigParams {
window_title: Some("3d Renderer".to_string()),
..EngineConfigParams::default()
}));
let cube = Mesh::cube();
let fov = 640.0;
let cam_pos = Vec3(0.0, 0.0, -5.0);
let mut rotation = Vec3(0.0, 0.0, 0.0);
println!("Start update");
eng.on_update(&mut |eng| {
eng.draw_grid(10, Some(0xFF333333));
rotation = rotation + Vec3(0.01, 0.02, 0.0);
let mut points = cube
.face_vertices()
.map(|fv| transform_points(&fv, rotation))
.flatten()
.collect();
points = project_points(&points, cam_pos, fov);
points.iter().for_each(|point| {
let mut x = point.x();
let mut y = point.y();
x += eng.config().width() as f64 / 2.0;
y += eng.config().height() as f64 / 2.0;
eng.draw_rect(x as usize, y as usize, 4, 4, 0xFFFF0000);
});
});
}
fn transform_points(points: &Vec<Vec3>, rotation: Vec3) -> Vec<Vec3> {
points.iter().map(|p| p.rot(rotation)).collect()
}
fn project_points(points: &Vec<Vec3>, cam_pos: Vec3, fov: f64) -> Vec<Vec3> {
points
.iter()
.map(|point| {
let z = point.z() + cam_pos.z();
let mut x = point.x() / z;
let mut y = point.y() / z;
x *= fov;
y *= fov;
Vec3(x, y, z)
})
.collect()
}
The problem is not the variables from the outer scope, it's the fact that the callback has mutable access to Engine but it itself comes from engine, so there are two coexisting mutable references. Theoretically, it could for example access the captured variables both by the data pointer in Engine and by using them directly from different threads, causing a data race.
Since you probably don't need access to the callback inside the callback, I would split Engine into two structs: one will contain the callback and the other, and have an update() and on_update() methods, and the other will include all other stuff. This way, the callback will take a mutable reference to the inner struct, which it can - since it is not contained within, but will still be able to call all engine's functions.
I'm making an iterator in rust. In the next() method I want to extract next_element as current_element which will be returned, and create new Term instance and set it as next_element:
pub struct Term {
pub coefficient: f64,
pub index: i32,
pub exponent: f64,
}
pub struct SeriesIterator {
next_element: Term,
}
impl Iterator for SeriesIterator {
type Item = Term;
fn next(&mut self) -> Option<Self::Item> {
let current_element = self.next_element;
self.next_element = Term {
coefficient: 1.0,
index: current_element.index + 1,
exponent: f64::from(current_element.index + 1),
};
Some(current_element)
}
}
How to move ownership of next_element into current_element which will finally be moved outside with the return of next()?
cannot move out of `self.next_element` which is behind a mutable reference
|
| let current_element = self.next_element;
| ^^^^^^^^^^^^^^^^
Using std::mem::replace, try this:
fn next(&mut self) -> Option<Self::Item> {
let index = self.next_element.index + 1;
Some(std::mem::replace(
&mut self.next_element,
Term {
coefficient: 1.0,
index: index,
exponent: f64::from(index),
},
))
}
Using #[derive(Clone, Copy)]:
fn next(&mut self) -> Option<Self::Item> {
let current_element = self.next_element; // copy
self.next_element.coefficient = 1.0;
self.next_element.index += 1;
self.next_element.exponent = f64::from(self.next_element.index);
Some(current_element)
}
Try this working example:
#[derive(Clone, Copy)]
pub struct Term {
pub coefficient: f64,
pub index: i32,
pub exponent: f64,
}
pub struct SeriesIterator {
next_element: Term,
}
impl Iterator for SeriesIterator {
type Item = Term;
fn next(&mut self) -> Option<Self::Item> {
let current_element = self.next_element; // copy
self.next_element.coefficient = 1.0;
self.next_element.index += 1;
self.next_element.exponent = f64::from(self.next_element.index);
Some(current_element)
}
}
fn main() {
let mut t = SeriesIterator {
next_element: Term {
coefficient: 1.0,
index: 0,
exponent: 1.0,
},
};
let next = t.next().unwrap();
println!("{}", next.index); // 0
println!("{}", t.next_element.index); // 1
let next = t.next().unwrap();
println!("{}", next.index); // 1
println!("{}", t.next_element.index); // 2
}
Output:
0
1
1
2