Je recherche une erreur dans le code tiers et je l'ai réduite à quelque chose dans le sens de.
use libc::c_void;
pub unsafe fn foo() {}
fn main() {
let ptr = &foo as *const _ as *const c_void;
println!("{:x}", ptr as usize);
}
Fonctionné sur la version 1.38.0 stable, cela affiche le pointeur de la fonction, mais la version bêta (1.39.0-beta.6) et la nuit renvoient «1». ( Aire de jeux )
À quoi est-il _
déduit et pourquoi le comportement a-t-il changé?
Je suppose que la bonne façon de lancer ce serait simplement foo as *const c_void
, mais ce n'est pas mon code.
types
casting
rust
undefined-behavior
Maciej Goszczycki
la source
la source
foo
est déjà un pointeur de fonction, vous ne devez donc pas lui attribuer d'adresse. Cela crée une double référence, apparemment à un type de taille nulle (donc la valeur magique1
).let ptr = foo as *const fn() as *const c_void;
Réponses:
Cette réponse est basée sur les réponses au rapport de bug motivées par cette question .
Chaque fonction dans Rust a son type d'élément de fonction individuel , qui est distinct du type d'élément de fonction de toutes les autres fonctions. Pour cette raison, une instance du type d'élément de fonction n'a pas besoin de stocker d'informations du tout - la fonction vers laquelle elle pointe est claire de son type. Donc, la variable x dans
est une variable de taille 0.
Les types d'élément de fonction contraignent implicitement les types de pointeur de fonction si nécessaire. La variable
est un pointeur générique vers n'importe quelle fonction avec signature
fn()
, et doit donc stocker un pointeur vers la fonction vers laquelle il pointe réellement, donc la taille dex
est la taille d'un pointeur.Si vous prenez l'adresse d'une fonction,
&foo
vous prenez en fait l'adresse d'une valeur temporaire de taille nulle. Avant cette validation dans lerust
référentiel , des temporaires de taille nulle avaient l'habitude de créer une allocation sur la pile et&foo
renvoyaient l'adresse de cette allocation. Depuis cette validation, les types de taille zéro ne créent plus d'allocations et utilisent plutôt l'adresse magique 1. Cela explique la différence entre les différentes versions de Rust.la source
fn
types d'éléments et les fermetures non capturantes et pour ceux-ci, il existe une solution de contournement, comme dans ma réponse, mais c'est toujours une arme à feu!*const i32
sur*const c_void
lequel, à ma connaissance, il est toujours garanti de préserver l'identité du pointeur.Chaque fois que vous effectuez une conversion de pointeur brut, vous ne pouvez modifier qu'une seule information (référence ou pointeur brut; mutabilité; type). Par conséquent, si vous effectuez ce casting:
puisque vous êtes passé d'une référence à un pointeur brut, le type déduit pour
_
doit être inchangé et est donc le type defoo
, qui est un type inexprimable pour la fonctionfoo
.Au lieu de cela, vous pouvez directement transtyper vers un pointeur de fonction, qui est exprimable dans la syntaxe Rust:
Quant à savoir pourquoi cela a changé, c'est difficile à dire. Cela pourrait être un bug dans la version nocturne. Cela vaut la peine de le signaler - même s'il ne s'agit pas d'un bogue, vous obtiendrez probablement une bonne explication de l'équipe du compilateur sur ce qui se passe réellement!
la source