유효하지 않은 포인터를 생성하는 함수 참조를 캐스트?


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-beta.6) 및 야간 반환 '1'이 표시됩니다. ( 운동장 )

_추론 은 무엇 이며 왜 행동이 바뀌 었습니까?

이것을 캐스팅하는 올바른 방법은 간단하다고 생각 foo as *const c_void하지만 이것은 내 코드가 아닙니다.


"변경된 이유"에 답할 수는 없지만 코드가 잘못되었다는 데 동의합니다. foo이미 함수 포인터이므로 주소를 가져 가지 않아야합니다. 크기가 0 인 유형 (이것은 마법의 가치 1)에 대한 이중 참조를 만듭니다 .
Shepmaster

이것은 귀하의 질문에 정확하게 대답하지는 않지만 다음을 원할 것입니다.let ptr = foo as *const fn() as *const c_void;
Peter Hall

답변:


3

이 답변은 이 질문에 대한 동기가 부여 된 버그 보고서 에 대한 답변을 기반으로합니다 .

Rust의 각 함수에는 개별 함수 항목 유형이 있으며 이는 다른 모든 함수의 함수 항목 유형과 다릅니다. 이러한 이유로 함수 항목 유형의 인스턴스는 정보를 전혀 저장할 필요가 없습니다. 어떤 유형의 기능인지는 명확하지 않습니다. 변수 x in

let x = foo;

크기가 0 인 변수입니다.

함수 아이템 타입 은 필요한 경우 함수 포인터 타입으로 암시 적으로 강제됩니다 . 변수

let x: fn() = foo;

은 signature 가 있는 모든 함수에 대한 일반적인 포인터 fn()이므로 실제로 가리키는 함수에 대한 포인터를 저장해야하므로 x크기는 포인터의 크기입니다.

함수의 주소를 사용하면 &foo실제로 크기가 0 인 임시 값의 주소를 사용하게됩니다. repo에 커밋rust 하기 전에 스택에서 할당을 생성하는 데 사용되는 0 크기의 임시 &foo는 해당 할당의 주소를 반환했습니다. 이 커밋 이후 크기가 0 인 유형은 더 이상 할당을 생성하지 않고 대신 매직 주소 1을 사용합니다. 이는 다른 버전의 Rust의 차이점을 설명합니다.


이것은 의미가 있지만, 일반적으로 바람직한 행동이 위태로운 가정에 기반을두고 있기 때문에 이것이 바람직한 것으로 확신하지 않습니다. 안전한 Rust 코드 내에서 ZST 값에 대한 포인터를 구별 할 이유가 없습니다. 컴파일시 알려진 값이 하나뿐이기 때문입니다. 여기에서와 같이 Rust 타입 시스템 외부에서 ZST 값을 사용해야하는 경우이 정보가 분류됩니다. fn항목 유형과 캡처하지 않는 클로저 에만 영향을 줄 수 있으며 내 대답과 같이 해결 방법이 있지만 여전히 상당히 중요합니다!
Peter Hall

좋아, Github 문제에 대한 새로운 답변을 읽지 못했습니다. 해당 코드로 segfault를 얻을 수 있지만 코드가 segfault를 유발할 수 있으면 새로운 동작이 정상이라고 생각합니다.
Peter Hall

좋은 대답입니다. @PeterHall 나는 같은 생각을하고 있었고 여전히 주제에 대해 100 %는 아니지만 적어도 임시 및 기타 스택 변수에 대해서는 컴파일러가 스택 레이아웃을 보장하고 어쨌든 ZST에 대한 포인터의 고유성을 보장 할 수 없습니다. 이것은 내가 이해하기에 여전히 포인터의 정체성을 보존한다고 보장되는 a *const i32를 캐스팅하는 것과 다릅니다 *const c_void.
trentcl

2

_추론 은 무엇 이며 왜 행동이 바뀌 었습니까?

원시 포인터 캐스트를 수행 할 때마다 하나의 정보 (참조 또는 원시 포인터, 가변성, 유형) 만 변경할 수 있습니다. 따라서이 캐스트를 수행하는 경우 :

let ptr = &foo as *const _

참조에서 원시 포인터 로 변경 했으므로 유추 된 유형 은 변경되지 _ 않아야 하므로 유형은 foo함수에 대해 표현할 수없는 유형입니다 foo.

그렇게하는 대신 Rust 구문으로 표현할 수있는 함수 포인터로 직접 캐스트 할 수 있습니다.

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

왜 바뀌 었는지 말하기는 어렵습니다. 야간 빌드의 버그 일 수 있습니다. 그것은 가치가 보고를 - 그것은 버그가없는 경우에도, 당신은 가능성이 실제로 무슨 일이 일어나고 있는지에 대한 컴파일러 팀에서 좋은 설명을 얻을 것이다!



@MaciejGoszczycki보고 해 주셔서 감사합니다! 응답은 실제로 나를 위해 명확 해졌습니다. 응답에 따라 답변을 게시하겠습니다.
Sven Marnach '17.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.