Играя с C или другими языками программирования, я всегда сталкиваюсь с ситуацией, когда невозможно избежать приведения типов, например. let c_var = rust_var as u32
, и я чувствую, что волнуюсь за выступление.
Выполняется ли приведение типов во время выполнения или компиляции? Использование встроенного as
не так эффективно?
Выполняется ли приведение типов во время выполнения или компиляции? Является ли использование в качестве встроенного не таким эффективным?
Обычно зависит от того, есть ли изменение представления. Если есть, то в актерском составе обязательно есть компонент времени выполнения, например. целое число -> целое число, целое число -> число с плавающей запятой, число с плавающей запятой -> целое число.
Однако эти преобразования обычно являются аппаратными операциями, возможно, даже аппаратными без операций, например. если вы конвертируете u16 в u32, на x64 компилятор просто сохранит u16 и прочитает u32 из того же регистра, возможно, используя mov-with-zero-extend (зависит от семантики регистров).
Если вы беспокоитесь, вы можете просто посмотреть, что выдает Godbolt, чтобы получить подсказки или доказательства. Например:
pub fn conv(num: u16) -> u32 {
num as _
}
pub fn conv2(num: u32) -> u64 {
num as _
}
example::conv:
movzx eax, di
ret
example::conv2:
mov eax, edi
ret
потому что на x86 16-битные (и 8-битные) регистры не расширяются до нуля, а 32-битные регистры:
- 32-битные операнды генерируют 32-битный результат, расширенный от нуля до 64-битного результата в целевом регистре общего назначения.
- 8-битные и 16-битные операнды генерируют 8-битный или 16-битный результат. Старшие 56 бит или 48 бит (соответственно) целевого регистра общего назначения не изменяются операцией. Если результат 8-битной или 16-битной операции предназначен для вычисления 64-битного адреса, явно расширьте регистр по знаку до полных 64-бит.
Таким образом, если данные «поступают» правильно, приведение не выполняется во время выполнения, несмотря на то, что оно происходит во время выполнения (поскольку оно соответствует поведению ISA по умолчанию). Если это не так, может потребоваться выполнение явной операции, например. u16 -> u32 на ARM64 требует явного обнуления старших 16 бит:
example::conv:
and w0, w0, #0xffff
ret
потому что ARM с самого начала была архитектурой 32b, однако это не проблема для u32 -> u64:
example::conv2:
mov w0, w0
ret
потому что в отличие от x86 он расширяется нулями.