rust 静态创建const Uuid from &str

flseospp  于 2023-05-29  发布在  其他
关注(0)|答案(2)|浏览(197)

我试图通过传入&str切片来在const上下文中创建Uuid,但我不断收到各种错误。
This answer给了我非常量的解决方案,但由于引用,它似乎在const中不起作用。
如何使用哈希的前16个字节作为Uuid的输入?这在const环境中实际上可能吗?

struct ExampleId(Uuid);

pub const fn from_str(value: &str) -> ExampleId {
    // Hash the str slice to get a "random" value
    let hash: const_sha1::Digest =
        const_sha1::sha1(&const_sha1::ConstBuffer::from_slice(value.as_bytes()));
    // This gives a 20 byte hash
    let bytes: [u8; 20] = hash.bytes();
    // We only need 16 of these for a UUID but it doesn't seem possible to do this in a const context
    let bytes_16: [u8; 16] = bytes[..16].try_into().unwrap();

    ExampleId(Uuid::from_bytes(bytes_16))
}

const EXAMPLE: ExampleId = ExampleId::from_str("example_id");
error[E0277]: the trait bound `[u8; 16]: From<&[u8]>` is not satisfied
  --> crates/bevy_diagnostic/src/diagnostic.rs:22:46
   |
22 |         let bytes_16: [u8; 16] = bytes[..16].try_into().unwrap();
   |                                              ^^^^^^^^ the trait `~const From<&[u8]>` is not implemented for `[u8; 16]`
   |
   = note: required for `&[u8]` to implement `~const Into<[u8; 16]>`
   = note: required for `[u8; 16]` to implement `~const TryFrom<&[u8]>`
   = note: required for `&[u8]` to implement `~const TryInto<[u8; 16]>`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `bevy_diagnostic` due to previous error

为了增加上下文,目前此代码使用u128,看起来像ExampleId::from_u128(0x8EE938B3184729691BCCD346823B631C),用户必须生成一个唯一的128位数字/十六进制。为了使它更容易,我希望允许传入一个字符串来生成ID。UUID是API的公开部分,因此不能更改,这意味着我需要从任意字符串切片生成UUID。最后,因为这个ID在编译时是必需的,所以这需要作为const函数来完成。

ogq8wdun

ogq8wdun1#

在const函数中还不允许使用for循环,但是你可以使用while循环:

let mut bytes_16 = [0; 16];
let mut i = 0;
while i < 16 {
    bytes_16[i] = bytes[i];
    i += 1;
}
hfwmuf9z

hfwmuf9z2#

我的意思是有this dumb solution copying the bytes one by one,也许在某个地方有一个宏:

let bytes_16: [u8; 16] = [
    bytes[0], bytes[1], bytes[2], bytes[3],
    bytes[4], bytes[5], bytes[6], bytes[7],
    bytes[8], bytes[9], bytes[10], bytes[11],
    bytes[12], bytes[13], bytes[14], bytes[15],
];

如果你愿意使用unafe,你也可以让编译器为你生成所有的复制代码:

// SAFETY: we assert that at least 16 elements are known good, so we can safely convert to a *const [_; 16] and dereference that.
let bytes_16 = unsafe {
    assert!(bytes.len() >= 16);
    *(bytes.as_ptr() as *const [_; 16])
};

与Sven Marnachs解决方案不同,这两种解决方案都不会对最终数组进行两次初始化,但它们都有其他缺点,无可否认,在这种情况下,除非您多次使用它,否则这并不是一个真正的问题。

相关问题