rust 为什么当两种类型具有完全相同的内存布局时,std::transmute::transmute不工作?

osh3o9ms  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(109)

让我先说一句,我正在积极地寻找除了transmute之外的其他选项,我只是惊讶于transmute在这种情况下无法编译。
我正在开发一个Builder派生库,它在编译时检查哪些字段被初始化。为此,我使用const泛型。我得到了基本的工作,但我试图添加一个功能,因为我正在寻找transmute
如果我使用transmute,setter的示例如下所示:

#[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) }
    }
}

但转化不是这样的。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看起来更像是这样,我只是在添加一个新特性时遇到了一些奇怪的生命周期问题(使用impl AsRef<T>作为setter输入类型)

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 }
    }
}
iyr7buue

iyr7buue1#

编译器中的分析限制太多了。它不允许任何依赖于动态大小的转换,即使我们可以证明大小相等。
但是,请注意,在您的情况下,转换是不正确的:你需要把#[repr(transparent)]添加到结构体中。

相关问题