Лиття посилання функції, що створює недійсний покажчик?


9

Я відслідковую помилку в коді сторонньої сторони, і я звузив її до чогось уздовж лінії.

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, але це не мій код.


Я не можу відповісти на "чому це змінилося", але я згоден з вами, що код починати неправильно. foo- це вже покажчик функції, тому не слід приймати до нього адресу. Це створює подвійну посилання, здавалося б, нульового типу (таким чином, магічне значення 1).
— Шепмайстер

Це точно не відповідає на ваше запитання, але ви, мабуть, хочете:let ptr = foo as *const fn() as *const c_void;
— Пітер Хол

Відповіді:


3

Ця відповідь ґрунтується на відповідях на повідомлення про помилку, мотивоване цим питанням .

Кожна функція в Rust має свій тип окремого функціонального елемента , який відрізняється від типу елемента функції кожної іншої функції. З цієї причини екземпляр типу функціонального елемента взагалі не потребує збереження інформації - на яку функцію він вказує, зрозуміло з його типу. Отже змінна x in

let x = foo;

є змінною розміру 0.

Типи елементів функції неявно примушують функціонувати типи вказівників, де це необхідно. Змінна

let x: fn() = foo;

є загальним вказівником на будь-яку функцію з підписом fn(), і тому потрібно зберігати вказівник на функцію, на яку вона фактично вказує, тому розмір x- це розмір вказівника.

Якщо ви приймаєте адресу функції, &fooви фактично приймаєте адресу нульового значення тимчасового значення. До цього приєднання до rustрепо , тимчасові нульові розміри використовували для створення виділення на стеку та &fooповертали адресу цього розподілу. Оскільки це робиться, нульові типи більше не створюють виділення, а замість цього використовують магічну адресу 1. Це пояснює різницю між різними версіями Rust.


Це має сенс, але я не переконаний, що це загально бажана поведінка, оскільки вона побудована на невпевненому припущенні. У безпечному коді Rust немає причин розрізняти покажчики на значення ZST - оскільки існує лише одне можливе значення, яке відомо під час компіляції. Це виходить з ладу, коли вам потрібно використовувати значення ZST поза системою типу Rust, наприклад, тут. Це, мабуть, стосується лише fnтипів предметів і неприхованих закриттів, і для тих, хто є вирішення, як у моїй відповіді, але це все ще досить пістолет!
— Пітер Хол

Гаразд, я не читав новіші відповіді на питання Github. Я міг би отримати segfault з цим кодом, але, якщо код може викликати segfault, то я думаю, що нова поведінка в порядку.
— Пітер Хол

Чудова відповідь. @PeterHall Я думав те саме, і я все ще не на 100% з цього приводу, але, принаймні, для тимчасових журналів та інших змінних стеків, не повинно виникнути проблем із встановленням усіх нульових значень на 0x1, оскільки компілятор не робить гарантії щодо компонування стека, і ви ніяк не можете гарантувати унікальність покажчиків на ZST. Це відрізняється від, скажімо, лиття, *const i32до *const c_voidякого, наскільки я розумію, все ще гарантується збереження ідентичності вказівника.
— trentcl

2

Про що робиться _висновок і чому поведінка змінилася?

Кожен раз, коли ви робите неочищений вказівник, ви можете змінити лише одну частину інформації (посилання чи необроблений покажчик; змінність; тип). Тому, якщо ви робите цей склад:

let ptr = &foo as *const _

оскільки ви змінилися з посилання на необроблений покажчик, тип зробленого висновку _ повинен бути незмінним і, отже, є типом foo, який є деяким невиразним типом для функції foo.

Замість цього ви можете безпосередньо перейти до покажчика функції, який виражається в синтаксисі Руста:

let ptr = foo as *const fn() as *const c_void;

Щодо того, чому він змінився, важко сказати. Це може бути помилка в нічній збірці. Варто повідомити про це - навіть якщо це не помилка, ви, швидше за все, отримаєте гарне пояснення від команди-компілятора про те, що відбувається насправді!


1
Дякую, я повідомив про це github.com/rust-lang/rust/isissue/65499
— Maciej Goszczycki

@MaciejGoszczycki Дякуємо за повідомлення! Відповіді насправді зрозуміли щось для мене - я опублікую відповідь на основі відповідей там.
— Свен Марнахч
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.