Geçersiz bir işaretçi üreten bir işlev başvurusu yayınlama?


9

Üçüncü taraf kodunda bir hatayı izliyorum ve satır boyunca bir şey için daralttım.

use libc::c_void;

pub unsafe fn foo() {}

fn main() {
    let ptr = &foo as *const _ as *const c_void;
    println!("{:x}", ptr as usize);
}

Kararlı 1.38.0 üzerinde koştu, bu işlev işaretçisini yazdırır, ancak beta (1.39.0-beta.6) ve gece '1' döndürür. ( Oyun Parkı )

_Çıkarım nedir ve neden davranış değişti?

Bu döküm için doğru yolu varsayalım foo as *const c_void, ama bu benim kodum değil.


"Neden değişti" cevap veremem, ama kod ile başlamak için yanlış olduğunu kabul ediyorum. foozaten bir işlev işaretçisi olduğundan, ona bir adres almamalısınız. Bu, görünüşte sıfır boyutlu bir türe (dolayısıyla sihirli değer 1) çift ​​referans oluşturur .
Shepmaster

Bu tam olarak sorunuza cevap vermiyor, ama muhtemelen:let ptr = foo as *const fn() as *const c_void;
Peter Hall

Yanıtlar:


3

Bu cevap, bu soru tarafından motive edilen hata raporundaki cevaplara dayanmaktadır .

Rust her işlevi vardır tek tek işlev öğesi türü her fonksiyon işlev öğesi türünden farklı olduğu,. Bu nedenle, işlev öğesi türünün bir örneğinin herhangi bir bilgi depolaması gerekmez - hangi işleve işaret ettiği türünden anlaşılır. Yani x değişkeni

let x = foo;

0 boyutunda bir değişkendir.

İşlev öğesi türleri , gerektiğinde işaretçi türlerine dolaylı olarak zorlar . Değişken

let x: fn() = foo;

imzalı herhangi bir işleve genel fn()bir işaretçi olduğundan, gerçekte işaret ettiği işleve bir işaretçi depolaması gerekir, bu nedenle xboyutu bir işaretçi boyutudur.

Bir işlevin adresini &fooalırsanız, aslında sıfır boyutlu geçici bir değerin adresini alıyorsunuz demektir. Bu rustrepo işleminden önce , sıfır boyutlu geçişler yığın üzerinde bir ayırma oluşturmak için kullanılır ve &foobu ayırmanın adresini döndürür. Bu işlemden bu yana, sıfır boyutlu türler artık ayırma oluşturmaz ve bunun yerine sihirli adres 1'i kullanır. Bu, Rust'un farklı sürümleri arasındaki farkı açıklar.


Bu mantıklı, ancak güvencesiz bir varsayım üzerine kurulu olduğu için genellikle istenen davranış olduğuna ikna olmadım. Güvenli Rust kodunda, işaretçileri bir ZST değerine ayırmak için bir neden yoktur - çünkü derleme zamanında bilinen tek bir olası değer vardır. Bu gibi Rust tipi sistemin dışında bir ZST değeri kullanmanız gerektiğinde bu bozulur. Muhtemelen sadece fnöğe türlerini ve yakalamayan kapanışları etkiler ve bunlar için cevabımda olduğu gibi bir çözüm var, ancak yine de bir tabanca!
Peter Hall

Tamam, Github konusunda yeni cevapları okumamıştım. Bu kod ile bir segfault alabilir ama kod bir segfault neden olabilir, o zaman yeni davranış tamam sanırım.
Peter Hall

Mükemmel cevap. @PeterHall Aynı şeyi düşünüyordum ve hala bu konuda% 100 değilim, ama en azından geçici arayüzler ve diğer yığın değişkenleri için, tüm sıfır boyutlu değerleri 0x1'e koymada sorun olmamalı çünkü derleyici yığın düzeniyle ilgili garantiler ve yine de ZST'lere yönelik işaretçilerin benzersizliğini garanti edemezsiniz. Bu döküm, demek farklıdır *const i32için *const c_voidbenim anlayış, hala ibrenin kimliğini korumak için garanti edilir, hangi.
trentcl

2

_Çıkarım nedir ve neden davranış değişti?

Ham işaretçi kadrosunu her yaptığınızda, yalnızca bir bilgi parçasını değiştirebilirsiniz (başvuru veya ham işaretçi; değiştirilebilirlik; tür). Bu nedenle, bu kadroyu yaparsanız:

let ptr = &foo as *const _

bir başvurudan ham işaretçiye değiştiğiniz için, için çıkartılan türün değiştirilmemesi _ gerekir ve bu nedenle fooişlev için ifade edilemeyen bir tür olan türdür foo.

Bunu yapmak yerine, Rust sözdiziminde ifade edilebilen bir işlev işaretçisine doğrudan yayın yapabilirsiniz:

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

Neden değiştiğine gelince, bunu söylemek zor. Her gece yapılan yapıda bir hata olabilir. Rapor etmeye değer - bir hata olmasa bile, derleyici ekibinden gerçekte neler olduğu hakkında iyi bir açıklama alacaksınız!



@MaciejGoszczycki Raporladığınız için teşekkürler! Yanıtlar aslında benim için net şeyler yaptı - oradaki yanıtlara dayanarak bir cevap göndereceğim.
Sven Marnach
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.