Ответ 1
TL; DR: Да, создание неинициализированной ссылки - это всегда неопределенное поведение. Вы не можете использовать mem::uninitialized
безопасно с generics. В настоящее время нет хорошего пути для вашего конкретного случая.
Запуск кода в valgrind сообщает о 3 ошибках, каждая из которых имеет одну и ту же трассировку стека:
==741== Conditional jump or move depends on uninitialised value(s)
==741== at 0x11907F: <alloc::vec::Vec<T> as alloc::vec::SpecExtend<T, I>>::spec_extend (vec.rs:1892)
==741== by 0x11861C: <alloc::vec::Vec<T> as alloc::vec::SpecExtend<&'a T, I>>::spec_extend (vec.rs:1942)
==741== by 0x11895C: <alloc::vec::Vec<T>>::extend_from_slice (vec.rs:1396)
==741== by 0x11C1A2: alloc::slice::hack::to_vec (slice.rs:168)
==741== by 0x11C643: alloc::slice::<impl [T]>::to_vec (slice.rs:369)
==741== by 0x118C1E: <alloc::vec::Vec<T> as core::clone::Clone>::clone (vec.rs:1676)
==741== by 0x11AF89: md::tests::check_drops (main.rs:51)
==741== by 0x119D39: md::__test::TESTS::{{closure}} (main.rs:36)
==741== by 0x11935D: core::ops::function::FnOnce::call_once (function.rs:223)
==741== by 0x11F09E: {{closure}} (lib.rs:1451)
==741== by 0x11F09E: call_once<closure,()> (function.rs:223)
==741== by 0x11F09E: <F as alloc::boxed::FnBox<A>>::call_box (boxed.rs:642)
==741== by 0x17B469: __rust_maybe_catch_panic (lib.rs:105)
==741== by 0x14044F: try<(),std::panic::AssertUnwindSafe<alloc::boxed::Box<FnBox<()>>>> (panicking.rs:289)
==741== by 0x14044F: catch_unwind<std::panic::AssertUnwindSafe<alloc::boxed::Box<FnBox<()>>>,()> (panic.rs:392)
==741== by 0x14044F: {{closure}} (lib.rs:1406)
==741== by 0x14044F: std::sys_common::backtrace::__rust_begin_short_backtrace (backtrace.rs:136)
Уменьшение при сохранении ошибки Valgrind (или одного очень похожего) приводит к
use std::{iter, mem};
fn main() {
let a = unsafe { mem::uninitialized::<&()>() };
let mut b = iter::once(a);
let c = b.next();
let _d = match c {
Some(_) => 1,
None => 2,
};
}
Выполнение этого меньшего воспроизведения в Miri на игровой площадке приводит к этой ошибке:
error[E0080]: constant evaluation error: attempted to read undefined bytes
--> src/main.rs:7:20
|
7 | let _d = match c {
| ^ attempted to read undefined bytes
|
note: inside call to 'main'
--> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:74:34
|
74| lang_start_internal(&move || main().report(), argc, argv)
| ^^^^^^
note: inside call to 'closure'
--> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:59:75
|
59| ::sys_common::backtrace::__rust_begin_short_backtrace(move || main())
| ^^^^^^
note: inside call to 'closure'
--> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/sys_common/backtrace.rs:136:5
|
13| f()
| ^^^
note: inside call to 'std::sys_common::backtrace::__rust_begin_short_backtrace::<[[email protected](1/1:1823 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>'
--> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:59:13
|
59| ::sys_common::backtrace::__rust_begin_short_backtrace(move || main())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: inside call to 'closure'
--> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:310:40
|
31| ptr::write(&mut (*data).r, f());
| ^^^
note: inside call to 'std::panicking::try::do_call::<[[email protected](1/1:1822 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>'
--> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:306:5
|
30| / fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
30| | unsafe {
30| | let data = data as *mut Data<F, R>;
30| | let f = ptr::read(&mut (*data).f);
31| | ptr::write(&mut (*data).r, f());
31| | }
31| | }
| |_____^
note: inside call to 'std::panicking::try::<i32, [[email protected](1/1:1822 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>'
--> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:392:9
|
39| panicking::try(f)
| ^^^^^^^^^^^^^^^^^
note: inside call to 'std::panic::catch_unwind::<[[email protected](1/1:1822 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>'
--> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:58:25
|
58| let exit_code = panic::catch_unwind(|| {
| _________________________^
59| | ::sys_common::backtrace::__rust_begin_short_backtrace(move || main())
60| | });
| |__________^
note: inside call to 'std::rt::lang_start_internal'
--> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:74:5
|
74| lang_start_internal(&move || main().report(), argc, argv)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Короткий вариант: mem::uninitialized
создает нулевой указатель, который рассматривается как ссылка. Это неопределенное поведение.
В исходном коде Vec::clone
реализуется путем итерации по итератору. Iterator::next
возвращает параметр Option<T>
, поэтому у вас есть опция ссылки, которая заставляет нулевую оптимизацию указателя входить. Это считается как None
, что рано заканчивает итерацию, в результате чего ваш пустой второй вектор.
Оказывается, имея mem::uninitialized
, фрагмент кода, который дает вам C-подобную семантику, представляет собой гигантский фуганок и часто используется неправильно (неожиданно!), Поэтому вы здесь не одиноки. Главные вещи, которые вы должны соблюдать в качестве замены: