提问人:Typhaon 提问时间:10/10/2023 最后编辑:Typhaon 更新时间:10/10/2023 访问量:91
当已知两种类型具有完全相同的内存布局时,为什么 std::mem::transmute 不起作用?
Why does std::mem::transmute not work when both types are known to have the exact same memory layout?
问:
让我先说一下,我正在积极寻找其他选择,我只是惊讶于在这种情况下无法编译。transmute
transmute
我正在开发一个派生库,该库在编译时检查哪些字段已初始化。为此,我使用了 const 泛型。我掌握了基础知识,但我正在尝试添加一个功能,因此我正在查看.Builder
transmute
如果我使用 ,setter 的示例如下所示:transmute
#[derive(Builder)]
pub struct Foo<const SIZE: usize> {
bar: [usize; SIZE],
}
// Just the setter part of the macro expansion:
impl<const SIZE: usize> FooBuilder<SIZE, false> {
pub fn bar(mut self, bar: [usize; SIZE]) -> FooBuilder<SIZE, true> {
self.data.bar = Some(bar);
unsafe { std::mem::transmute(self) }
}
}
但是 transmute 不是这样工作的。 完全相同,布尔值对底层数据没有任何影响,但我仍然得到:SIZE
cannot transmute between types of different sizes, or dependently-sized types
source type: `test::const_generic::FooBuilder<SIZE, false>` (size can vary because of [usize; SIZE])
target type: `test::const_generic::FooBuilder<SIZE, true>` (size can vary because of [usize; SIZE])
我只是好奇为什么会有这个限制。谁能向我解释一下?
// simplified macro expansion:
pub struct Foo<const SIZE: usize> {
bar: [usize; SIZE],
}
impl <const SIZE: usize> Foo<SIZE> {
fn builder() -> FooBuilder<SIZE, false> {
FooBuilder::new()
}
}
pub struct FooBuilder<const SIZE: usize, const __BUILDER_CONST: bool> {
bar: Option<[usize; SIZE]>,
}
impl<const SIZE: usize> FooBuilder<SIZE, false> {
pub fn new() -> FooBuilder<SIZE, false> {
FooBuilder {
bar: None,
}
}
}
impl<const SIZE: usize> FooBuilder<SIZE, false> {
pub fn bar(mut self, bar: [usize; SIZE]) -> FooBuilder<SIZE, true> {
self.bar = Some(bar);
unsafe { std::mem::transmute(self) }
// ^ Should work, because the value of `__BUILDER_CONST` has no influence
// on the layout of the struct, and `SIZE` is identical
}
}
impl<const SIZE: usize> FooBuilder<SIZE, true> {
pub fn build(self) -> Foo<SIZE> {
Foo::<SIZE> {
bar: self.bar.unwrap()
}
}
}
顺便说一句,实际的 setter 看起来更如下,我只是在添加新功能(用作 setter 输入类型)时遇到了一些奇怪的生命周期问题impl AsRef<T>
impl<const SIZE: usize> FooBuilder<SIZE, false> {
pub fn bar(self, bar: [usize; SIZE]) -> FooBuilder<SIZE, true> {
let mut data = self.data;
data.bar = Some(bar);
FooBuilder { data }
}
}
答:
1赞
Chayim Friedman
10/10/2023
#1
编译器中的分析限制性太强了。它不允许任何依赖于动态大小的转换,即使我们可以证明大小相等。
但是,请注意,在您的情况下,转换是不正确的:您需要添加到结构中。#[repr(transparent)]
评论
FooBuilder<_, false>
FooBuilder<_, true>
transmute_unchecked
transmute
impl AsRef<str> +'a
transmute_unchecled
transmute
unsafe
String
'static
bar
&'a impl AsRef<str>
&'a str
&
.as_ref()