Estou rastreando um erro no código de terceiros e o reduzi a algo ao longo das linhas 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);
}
Executado no estável 1.38.0, imprime o ponteiro da função, mas beta (1.39.0-beta.6) e o retorno noturno '1'. ( Parque infantil )
O que é _
inferido e por que o comportamento mudou?
Presumo que a maneira correta de transmitir isso seria simplesmente foo as *const c_void
, mas esse não é o meu código.
types
casting
rust
undefined-behavior
Maciej Goszczycki
fonte
fonte
foo
já é um ponteiro de função, portanto, você não deve levar um endereço para ele. Isso cria uma referência dupla, aparentemente para um tipo de tamanho zero (portanto, o valor mágico1
).let ptr = foo as *const fn() as *const c_void;
Respostas:
Esta resposta é baseada nas respostas do relatório de erros motivadas por esta pergunta .
Cada função no Rust possui seu tipo de item de função individual , que é distinto do tipo de item de função de todas as outras funções. Por esse motivo, uma instância do tipo de item de função não precisa armazenar nenhuma informação - para qual função ela aponta é clara em seu tipo. Então a variável x em
é uma variável de tamanho 0.
Os tipos de itens de função coagem implicitamente para tipos de ponteiros de função, quando necessário. A variável
é um ponteiro genérico para qualquer função com assinatura
fn()
e, portanto, precisa armazenar um ponteiro na função para a qual ele realmente aponta; portanto, o tamanho dex
é o tamanho de um ponteiro.Se você pegar o endereço de uma função,
&foo
na verdade, está usando o endereço de um valor temporário de tamanho zero. Antes de este se comprometer com arust
repo , temporários de tamanho zero usado para criar uma alocação na pilha, e&foo
devolveu o endereço dessa alocação. Desde essa confirmação, os tipos de tamanho zero não criam mais alocações e, em vez disso, usam o endereço mágico 1. Isso explica a diferença entre as diferentes versões do Rust.fonte
fn
tipos de itens e os fechamentos que não capturam e, para aqueles, existe uma solução alternativa, como na minha resposta, mas ainda é uma arma de fogo!*const i32
para o*const c_void
qual, no meu entender, ainda é garantido preservar a identidade do ponteiro.Cada vez que você faz uma conversão de ponteiro bruto, você pode alterar apenas uma informação (referência ou ponteiro bruto; mutabilidade; tipo). Portanto, se você fizer este elenco:
desde que você mudou de uma referência para um ponteiro bruto, o tipo inferido
_
deve permanecer inalterado e, portanto, é o tipo defoo
, que é um tipo inexprimível para a funçãofoo
.Em vez de fazer isso, você pode converter diretamente em um ponteiro de função, expressável na sintaxe Rust:
Quanto à razão pela qual mudou, é difícil dizer. Pode ser um bug na compilação noturna. Vale a pena denunciá-lo - mesmo que não seja um bug, você provavelmente obterá uma boa explicação da equipe do compilador sobre o que realmente está acontecendo!
fonte