Estoy rastreando un error en el código de un tercero y lo reduje a algo similar.
use libc::c_void;
pub unsafe fn foo() {}
fn main() {
let ptr = &foo as *const _ as *const c_void;
println!("{:x}", ptr as usize);
}
Ejecutado en estable 1.38.0 esto imprime el puntero de función, pero beta (1.39.0-beta.6) y nocturno devuelven '1'. ( Área de juegos )
¿A qué se _
infiere y por qué ha cambiado el comportamiento?
Supongo que la forma correcta de emitir esto sería simplemente foo as *const c_void
, pero este no es mi código.
types
casting
rust
undefined-behavior
Maciej Goszczycki
fuente
fuente
foo
ya es un puntero de función, por lo que no debe tomar una dirección. Eso crea una doble referencia, aparentemente a un tipo de tamaño cero (por lo tanto, el valor mágico1
).let ptr = foo as *const fn() as *const c_void;
Respuestas:
Esta respuesta se basa en las respuestas en el informe de error motivado por esta pregunta .
Cada función en Rust tiene su tipo de elemento de función individual , que es distinto del tipo de elemento de función de cualquier otra función. Por esta razón, una instancia del tipo de elemento de función no necesita almacenar ninguna información; la función a la que apunta está clara a partir de su tipo. Entonces la variable x en
es una variable de tamaño 0.
Los tipos de elementos de función obligan implícitamente a los tipos de puntero de función cuando sea necesario. La variable
es un puntero genérico a cualquier función con firma
fn()
y, por lo tanto, necesita almacenar un puntero a la función a la que apunta realmente, por lo que el tamaño dex
es el tamaño de un puntero.Si toma la dirección de una función, en
&foo
realidad está tomando la dirección de un valor temporal de tamaño cero. Antes de comprometerse con elrust
repositorio , los temporales de tamaño cero solían crear una asignación en la pila y&foo
devolvían la dirección de esa asignación. Desde esta confirmación, los tipos de tamaño cero ya no crean asignaciones, y en su lugar usan la dirección mágica 1. Esto explica la diferencia entre las diferentes versiones de Rust.fuente
fn
tipos de elementos y los cierres que no capturan, y para aquellos hay una solución alternativa, como en mi respuesta, ¡pero sigue siendo una pistola de pie!*const i32
al*const c_void
que, según tengo entendido, todavía se garantiza que preservará la identidad del puntero.Cada vez que realiza una conversión de puntero sin formato, solo puede cambiar una pieza de información (referencia o puntero sin formato; mutabilidad; tipo). Por lo tanto, si haces este reparto:
Como ha cambiado de una referencia a un puntero sin formato, el tipo inferido para no
_
debe modificarse y, por lo tanto, es el tipo defoo
, que es un tipo inexpresable para la funciónfoo
.En lugar de hacerlo, puede convertir directamente a un puntero de función, que se puede expresar en la sintaxis de Rust:
En cuanto a por qué ha cambiado, eso es difícil de decir. Podría ser un error en la construcción nocturna. Vale la pena informarlo , incluso si no es un error, es probable que obtenga una buena explicación del equipo del compilador sobre lo que realmente está sucediendo.
fuente