是否可以在 Rust 中将一个结构的内存与另一个结构相关联?
Is it possible to associate memory from one struct with another in Rust?
我知道在 Rust 中,编译器不保证您按照声明的顺序获取结构数据,以节省内存(我也相信一些 C 代码优化器正在做同样的事情)。假设现在我有一个二叉树并想将其转换为双向链表。在 C 中我会声明两个结构:
typedef struct tree{
void* left_child;
void* right_child;
void* data;
}tree_t;
对于树,并且:
typedef struct list{
void* before;
void* after;
void* data;
}list_t;
为链表。如果我现在想将树转换为列表,我可以就地执行此操作,我只需将树的内存与列表结构相关联并更改指针:
tree_t mytree;
/*fill tree*/
list_t *list_p;
list_p = (list_t)&mytree;
/*change pointers accordingly*/
但是我怎么能在 Rust 中做这样的事情呢?甚至可以不使用 unsafe
代码吗?
直到现在我有了我的树:
struct TreeNode<'a, T> {
left_child: BinaryTreeLink<'a, T>,
right_child: BinaryTreeLink<'a, T>,
data : &'a T,
}
type BinaryTreeLink<'a, T> = Option<Box<TreeNode<'a, T>>>;
列表为:
struct ListNode<'a, T> {
before: ListLink<'a, T>,
after: ListLink<'a, T>,
data : &'a T,
}
type ListLink<'a, T> = Option<Box<ListNode<'a, T>>>;
但是现在我怎样才能有效地转换它们呢?
But How can I do such a thing in Rust? Is it even possible without using unsafe code? Till now I have my tree
要直接做同样的事情,您需要使用不安全的代码。函数 std::mem::transmute
完全符合您的要求。问题是 Rust 中结构的布局是无法保证的,所以下面通常是未定义的行为:
use std::mem;
let list_link: Option<Box<ListNode<_>>> = unsafe { mem::transmute(tree_node) };
但是,您可以通过使用 C
表示强制结构的布局可预测来使其安全:
#[repr(C)]
struct TreeNode<'a, T> {
left_child: BinaryTreeLink<'a, T>,
right_child: BinaryTreeLink<'a, T>,
data : &'a T,
}
#[repr(C)]
struct ListNode<'a, T> {
before: ListLink<'a, T>,
after: ListLink<'a, T>,
data : &'a T,
}
您还需要将 #[repr(C)]
应用于内部类型 ListLink
和 BinaryTreeLink
的定义。
但是如何完全避免不安全的代码呢?如果你编写转换函数,消耗原始数据,优化器应该能够将它变成一个空操作,因为它知道没有其他代码可能正在引用该内存。
<'a, T> impl From<ListNode<'a, T>> for TreeNode<'a, T> {
fn from(other: ListNode<'a, T>) -> ListNode<'a, T>> {
ListNode {
before: other.left_child,
after: other.right_child,
data: other.data,
}
}
}
你绝对应该对此进行基准测试以确保,但优化器拥有将其变成空操作所需的所有信息,而且它很可能会。
我知道在 Rust 中,编译器不保证您按照声明的顺序获取结构数据,以节省内存(我也相信一些 C 代码优化器正在做同样的事情)。假设现在我有一个二叉树并想将其转换为双向链表。在 C 中我会声明两个结构:
typedef struct tree{
void* left_child;
void* right_child;
void* data;
}tree_t;
对于树,并且:
typedef struct list{
void* before;
void* after;
void* data;
}list_t;
为链表。如果我现在想将树转换为列表,我可以就地执行此操作,我只需将树的内存与列表结构相关联并更改指针:
tree_t mytree;
/*fill tree*/
list_t *list_p;
list_p = (list_t)&mytree;
/*change pointers accordingly*/
但是我怎么能在 Rust 中做这样的事情呢?甚至可以不使用 unsafe
代码吗?
直到现在我有了我的树:
struct TreeNode<'a, T> {
left_child: BinaryTreeLink<'a, T>,
right_child: BinaryTreeLink<'a, T>,
data : &'a T,
}
type BinaryTreeLink<'a, T> = Option<Box<TreeNode<'a, T>>>;
列表为:
struct ListNode<'a, T> {
before: ListLink<'a, T>,
after: ListLink<'a, T>,
data : &'a T,
}
type ListLink<'a, T> = Option<Box<ListNode<'a, T>>>;
但是现在我怎样才能有效地转换它们呢?
But How can I do such a thing in Rust? Is it even possible without using unsafe code? Till now I have my tree
要直接做同样的事情,您需要使用不安全的代码。函数 std::mem::transmute
完全符合您的要求。问题是 Rust 中结构的布局是无法保证的,所以下面通常是未定义的行为:
use std::mem;
let list_link: Option<Box<ListNode<_>>> = unsafe { mem::transmute(tree_node) };
但是,您可以通过使用 C
表示强制结构的布局可预测来使其安全:
#[repr(C)]
struct TreeNode<'a, T> {
left_child: BinaryTreeLink<'a, T>,
right_child: BinaryTreeLink<'a, T>,
data : &'a T,
}
#[repr(C)]
struct ListNode<'a, T> {
before: ListLink<'a, T>,
after: ListLink<'a, T>,
data : &'a T,
}
您还需要将 #[repr(C)]
应用于内部类型 ListLink
和 BinaryTreeLink
的定义。
但是如何完全避免不安全的代码呢?如果你编写转换函数,消耗原始数据,优化器应该能够将它变成一个空操作,因为它知道没有其他代码可能正在引用该内存。
<'a, T> impl From<ListNode<'a, T>> for TreeNode<'a, T> {
fn from(other: ListNode<'a, T>) -> ListNode<'a, T>> {
ListNode {
before: other.left_child,
after: other.right_child,
data: other.data,
}
}
}
你绝对应该对此进行基准测试以确保,但优化器拥有将其变成空操作所需的所有信息,而且它很可能会。