Ответ 1
Проверка времени компиляции?
Есть ли лучший способ сделать это, или это проверка времени выполнения, которую поддерживает лучший Rust 1.0?
Короче: Нет, сейчас нет лучшего способа (начиная с Rust 1.10). В настоящее время проверка времени выполнения - это лучшее, что мы можем сделать.
Однако есть надежда: удивительная "const-зависимая система типов" RFC, скорее всего, сделает случаи, которые можно проверить во время компиляции, Я попытаюсь вспомнить обновление моего ответа после принятия и внедрения RFC (надеюсь, это будет!).
Как насчет проверки времени выполнения?
Как сказано в комментариях bluss, существует проверка времени выполнения, поскольку константа оптимизатора сбрасывает чек. Пусть тестовый оператор с этим кодом:
#![feature(asm)]
fn main() {
foo(3u64);
foo(true);
}
#[inline(never)]
fn foo<T>(t: T) {
use std::mem::size_of;
unsafe { asm!("" : : "r"(&t)) }; // black box
assert!(size_of::<usize>() == size_of::<T>());
unsafe { asm!("" : : "r"(&t)) }; // black box
}
Сумасшедшие выражения asm!()
служат двум целям:
- "скрывать"
t
от LLVM, так что LLVM не может выполнять оптимизации, которые мы не хотим (например, удаление всей функции) - маркировка определенных точек в полученном ASM-коде, который мы рассмотрим
Скомпилируйте его с помощью ночного компилятора (в 64-битной среде!):
rustc -O --emit=asm test.rs
Как обычно, полученный код сборки трудно читать; вот важные места (с некоторой очисткой):
_ZN4test4main17he67e990f1745b02cE: # main()
subq $40, %rsp
callq _ZN4test3foo17hc593d7aa7187abe3E
callq _ZN4test3foo17h40b6a7d0419c9482E
ud2
_ZN4test3foo17h40b6a7d0419c9482E: # foo<bool>()
subq $40, %rsp
movb $1, 39(%rsp)
leaq 39(%rsp), %rax
#APP
#NO_APP
callq _ZN3std9panicking11begin_panic17h0914615a412ba184E
ud2
_ZN4test3foo17hc593d7aa7187abe3E: # foo<u64>()
pushq %rax
movq $3, (%rsp)
leaq (%rsp), %rax
#APP
#NO_APP
#APP
#NO_APP
popq %rax
retq
Пара #APP
- #NO_APP
- наше выражение asm!()
.
- Случай
foo<bool>
: вы можете видеть, что наша первая командаasm!()
скомпилирована, затем выполняется безусловный вызовpanic!()
, а потом ничего не приходит (ud2
просто говорит: "Программа никогда не сможет достичь этого места,panic!()
расходится" ). - Случай
foo<u64>
: вы можете видеть пары#APP
-#NO_APP
(оба выраженияasm!()
) без каких-либо промежутков между ними.
Итак, да: компилятор полностью удаляет проверку.
Было бы лучше, если бы компилятор просто отказался компилировать код. Но мы, по крайней мере, знаем, что накладных расходов нет.