我尝试将Vec
u32
转换为Vec
u8
个Vec
,最好是就地而且没有太多开销。< / p>
我当前的解决方案依赖于不安全的代码来重新构建use std::mem;
use std::vec::Vec;
fn main() {
let mut vec32 = vec![1u32, 2];
let vec8;
unsafe {
let length = vec32.len() * 4; // size of u8 = 4 * size of u32
let capacity = vec32.capacity() * 4; // ^
let mutptr = vec32.as_mut_ptr() as *mut u8;
mem::forget(vec32); // don't run the destructor for vec32
// construct new vec
vec8 = Vec::from_raw_parts(mutptr, length, capacity);
}
println!("{:?}", vec8)
}
。有没有更好的方法来做到这一点,以及与我的解决方案相关的风险是什么?
//app.js
function newDoc(event) {
alert(this.className)
var root = document.getElementById('root'),
originTree = root.innerHTML;
root.innerHTML = ''
//create all needed element
var previewBox1 = document.createElement('div'),
previewBox2 = document.createElement('div'),
previewCommon = document.createElement('div') ,
tankImg = document.createElement('div'),
previewLabel = document.createElement('div'),
backToList = document.createElement('div'),
table = document.createElement('div'),
previewBox1Flag = document.createElement('div'),
previewBox1Model = document.createElement('div'),
previewBox1Level =
document.createElement('div');
// find tank object by Model name
for (let i = 0; i < tanks.length; ++i){
if (tanks[i].model === this.className){
var clickedObj = tanks[i];
break;
}
}
// fill by data object
tankImg.style.backgroundImage = "url(" + clickedObj.preview + ")";
//in previous line is my trouble!
previewBox1Flag.style.backgroundImage = "url(" +
clickedObj["country_image"] + ")";
previewBox1Level.innerHTML = "(level " + clickedObj.level + ")";
previewBox1Model.innerHTML = clickedObj.model.toUpperCase();
backToList.innerHTML = 'Back to list view';
previewLabel.innerHTML = 'Preview';
table.innerHTML = 'Characteristic';
// to construck all the element
previewCommon.appendChild(previewBox1Flag);
previewCommon.appendChild(previewBox1Model);
previewCommon.appendChild(previewBox1Level);
previewBox1.appendChild(previewCommon);
previewBox1.appendChild(previewLabel);
previewBox1.appendChild(tankImg);
previewBox1.appendChild(backToList);
previewBox2.appendChild(table);
root.appendChild(previewBox1);
root.appendChild(previewBox2);
}
function makePreviewObject(list){
var rootDiv = document.getElementById('root'),
newUl = document.createElement('ul');
for (let i = 0;i < list.length; ++i){
var nextLi = document.createElement('li'),
newImgDiv = document.createElement('div'),
newCommonDiv = document.createElement('div'),
divForFlag = document.createElement('div'),
divForLevel = document.createElement('div'),
divForModel = document.createElement('div');
nextLi.className = list[i].model;
newImgDiv.className = 'newImgDiv';
newCommonDiv.className = 'newCommonDiv';
divForFlag.className = 'divForFlag';
newImgDiv.style.backgroundImage = 'url(' + list[i].preview + ')';
divForFlag.style.backgroundImage = 'url(' + list[i]['country_image']
+ ')';
divForLevel.innerHTML = list[i].level;
divForModel.innerHTML = lengthOffSet(list[i].model.toUpperCase());
newCommonDiv.appendChild(divForFlag);
newCommonDiv.appendChild(divForLevel);
newCommonDiv.appendChild(divForModel);
nextLi.addEventListener('click', newDoc);
nextLi.appendChild(newImgDiv);
nextLi.appendChild(newCommonDiv);
newUl.appendChild(nextLi);
rootDiv.appendChild(newUl);
}
}
function lengthOffSet(string){
if (string.length < 14){
return string
} else{
return string.substr(0, 11) + '...'
}
}
makePreviewObject(tanks);
//tanks.js
var tanks = [
{
model: "Object 140",
preview: "images/tanks/object_140.png",
country: "ussr",
country_image: "images/countries/ussr.png",
level: 10,
details: {
damage: 420,
breoning: 264,
attack_speed: 9.09,
time_of_targeting: 2.1,
ammunition: 50
}
},
{
model: "t-62a",
preview: "images/tanks/t62a.png",
country: "ussr",
country_image: "images/countries/ussr.png",
level: 10,
details: {
damage: 630,
breoning: 246,
attack_speed: 9.09,
time_of_targeting: 2,
ammunition: 50
}
}
]
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="root"></div>
<script src="tanks.js"></script>
<script src="app.js"></script>
</body>
</html>
答案 0 :(得分:6)
每当编写unsafe
块时,我强烈鼓励人们在块上添加注释,解释为什么您认为代码实际上是安全的 。这类信息对将来阅读代码的人很有用。
只需使用mem::size_of::<u32>
,而不是添加有关“幻数”4的评论。我甚至会为size_of
使用u8
并执行除法以获得最大的清晰度。
您可以从unsafe
区块返回新创建的Vec。
正如评论中所提到的,“倾销”这样的数据块使得数据格式平台依赖;你将在小端和大端系统上得到不同的答案。这可能导致未来的大量调试问题。文件格式要么将平台字节序编码到文件中(使读者的工作更难),要么只对文件写一个特定的内容(使作者的工作更难)。
我可能会将整个unsafe
块移动到一个函数中并为其命名,仅用于组织目的。
您无需导入Vec
,它就在前奏中。
use std::mem;
fn main() {
let mut vec32 = vec![1u32, 2];
// I copy-pasted this code from StackOverflow without reading the answer
// surrounding it that told me to write a comment explaining why this code
// is actually safe for my own use case.
let vec8 = unsafe {
let ratio = mem::size_of::<u32>() / mem::size_of::<u8>();
let length = vec32.len() * ratio;
let capacity = vec32.capacity() * ratio;
let ptr = vec32.as_mut_ptr() as *mut u8;
// Don't run the destructor for vec32
mem::forget(vec32);
// Construct new Vec
Vec::from_raw_parts(ptr, length, capacity)
};
println!("{:?}", vec8)
}
我对此代码最大的未知担心在于与Vec
关联的内存对齐。
Rust的底层分配器allocates和deallocates内存特定Layout
。 Layout
包含指针的 size 和 alignment 等信息。
我认为此代码需要Layout
才能在对alloc
和dealloc
的成对调用之间进行匹配。如果是这种情况,请dropping the Vec<u8>
constructed from a Vec<u32>
might tell the allocator the wrong alignment,因为该信息为based on the element type。
如果没有更好的知识,“最好”的做法就是让Vec<u32>
保持原样并简单地获得&[u8]
。切片与分配器没有交互,避免了这个问题。
另见:
答案 1 :(得分:0)
如果就地转换不是强制性的,那么像这样管理bytes order控制并避免不安全的阻止:
extern crate byteorder;
use byteorder::{WriteBytesExt, BigEndian};
fn main() {
let vec32: Vec<u32> = vec![0xaabbccdd, 2];
let mut vec8: Vec<u8> = vec![];
for elem in vec32 {
vec8.write_u32::<BigEndian>(elem).unwrap();
}
println!("{:?}", vec8);
}