Rust移植终端3D甜甜圈动画出现字符残留问题求助
Rust移植3D甜甜圈动画字符残留问题修复
问题根源
char类型内存填充错误:Rust的char是4字节Unicode标量值,原代码用std::ptr::write_bytes仅填充每个char的首字节为空格(32u8),剩余3字节未清零,导致旧字符数据残留。- 数组越界访问:输出循环中
k < 1761会访问b[1760],超出数组(长度1760)的有效索引范围(0-1759)。 - 角度增量逻辑错误:每输出一个字符就递增旋转角度
A和B,导致单帧内角度不断变化,动画异常。 - 不必要的unsafe操作:用unsafe方式重置数组容易出错,Rust提供了更安全的替代方案。
修正后的代码
use std::{io, thread, time}; use clearscreen; fn main() { let stdout = io::stdout(); let mut handle = stdout.lock(); let mut A: f32 = 0.0; let mut B: f32 = 0.0; let mut z: [f32; 1760] = [0.0; 1760]; // 用u8数组替代char数组,匹配原C代码的单字节字符逻辑 let mut b: [u8; 1760] = [b' '; 1760]; // 将字符集提到循环外,避免重复创建 let chars = [b'.', b',', b'-', b'~', b':', b';', b'=', b'!', b'*', b'#', b'$', b'@']; loop { // 安全重置数组,替代unsafe的write_bytes b.fill(b' '); z.fill(0.0); let mut j = 0.0; while j < 6.28 { let mut i = 0.0; while i < 6.28 { let c = f32::sin(i); let d = f32::cos(j); let e = f32::sin(A); let f = f32::sin(j); let g = f32::cos(A); let h = d + 2.0; let D = 1.0 / (c * h * e + f * g + 5.0); let l = f32::cos(i); let m = f32::cos(B); let n = f32::sin(B); let t = c * h * g - f * e; let x = (40.0 + 30.0 * D * (l * h * m - t * n)) as i32; let y = (12.0 + 15.0 * D * (l * h * n + t * m)) as i32; let o = x + 80 * y; let N = (8.0 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n)) as i32; if y > 0 && y < 22 && x > 0 && x < 80 && D > z[o as usize] { z[o as usize] = D; if N > 0 && N < chars.len() as i32 { b[o as usize] = chars[N as usize]; } else { b[o as usize] = b' '; } } i += 0.02; } j += 0.07; } // 输出帧内容,修正越界问题 for k in 0..1760 { if (k + 1) % 80 == 0 { handle.write_all(&[b[k], b'\n']).unwrap(); } else { handle.write_all(&[b[k]]).unwrap(); } } // 单帧完成后再更新旋转角度 A += 0.04; B += 0.02; thread::sleep(time::Duration::from_millis(30)); clearscreen::clear().unwrap(); } }
关键修改点
- 将
char数组改为u8数组,匹配原C代码的单字节字符逻辑,避免Unicode字符的内存填充问题。 - 用
fill方法安全重置数组,移除unsafe操作。 - 修正输出循环的越界问题,调整换行逻辑为每80个字符输出换行。
- 将旋转角度的增量移到单帧绘制完成后,保证一帧内角度稳定。
- 将字符集向量改为全局数组,避免循环内重复创建。
内容的提问来源于stack exchange,提问作者Locu
相关产品推荐
相关产品推荐

