Я отслеживаю ошибку в стороннем коде и сузил ее до чего-то вроде.
use libc::c_void;
pub unsafe fn foo() {}
fn main() {
let ptr = &foo as *const _ as *const c_void;
println!("{:x}", ptr as usize);
}
При запуске на стабильной версии 1.38.0 выводится указатель на функцию, но бета (1.39.0-бета.6) и ночной возврат '1'. ( Детская площадка )
На что делается _
вывод и почему изменилось поведение?
Я предполагаю, что правильный способ разыграть это будет просто foo as *const c_void
, но это не мой код.
types
casting
rust
undefined-behavior
Мацей Гошицкий
источник
источник
foo
это уже указатель на функцию, поэтому вы не должны принимать к нему адрес. Это создает двойную ссылку, по-видимому, на тип нулевого размера (таким образом, магическое значение1
).let ptr = foo as *const fn() as *const c_void;
Ответы:
Этот ответ основан на ответах на сообщение об ошибке, мотивированных этим вопросом .
Каждая функция в Rust имеет свой отдельный тип элемента функции , который отличается от типа элемента функции любой другой функции. По этой причине экземпляру типа функционального элемента вообще не нужно хранить какую-либо информацию - на какую функцию он указывает, ясно из его типа. Таким образом, переменная х в
переменная размера 0.
Типы элементов функций неявно приводят к типам указателей на функции там, где это необходимо. Переменная
является универсальным указателем на любую функцию с сигнатурой
fn()
и, следовательно, должен хранить указатель на функцию, на которую он фактически указывает, поэтому размерx
равен размеру указателя.Если вы берете адрес функции,
&foo
вы фактически берете адрес временного значения нулевого размера. Перед этой фиксацией вrust
репо временные объекты нулевого размера использовали для создания выделения в стеке и&foo
возвращали адрес этого распределения. После этой фиксации типы нулевого размера больше не создают выделений, а вместо этого используют магический адрес 1. Это объясняет разницу между различными версиями Rust.источник
fn
типы элементов и не захватывающие замыкания, и для тех, у кого есть обходной путь, как в моем ответе, но это все еще довольно пушечный пистолет!*const i32
к*const c_void
которому, на мой взгляд, все еще гарантированно сохраняется идентичность указателя.Каждый раз, когда вы выполняете приведение необработанного указателя, вы можете изменить только один фрагмент информации (ссылочный или необработанный указатель; изменчивость; тип). Поэтому, если вы делаете это приведение:
так как вы изменили ссылку на необработанный указатель, выведенный для него тип
_
должен быть неизменным и, следовательно, является типомfoo
, который является неописуемым типом для функцииfoo
.Вместо этого вы можете напрямую привести к указателю на функцию, которая выражается в синтаксисе Rust:
Что касается того, почему это изменилось, сказать сложно. Это может быть ошибка в ночной сборке. Стоит сообщить об этом - даже если это не ошибка, вы, вероятно, получите хорошее объяснение от команды компиляторов о том, что на самом деле происходит!
источник