Here's my best safe attempt in Rust so far:
// alloca and initialize a potentially huge array of 'Drop' objects.
// Parse a usize constant from a numeric environment variable.
const fn parse_usize(s: &str) -> usize
{ let mut out:usize = 0; let mut i:usize = 0;
while i<s.len() { out *= 10; out += (s.as_bytes()[i] - b'0') as usize; i += 1; }
out }
const EBYTES: &'static str = env!("SBYTES");
const SBYTES: usize = parse_usize(EBYTES);
const STEP: usize = 2;
// An example of a Drop (=!Copy) object which should not be moved.
mod no_copy
{
use std::ptr::{null};
use std::fmt::{Debug,Error,Formatter};
pub struct MyDrop { p: *const MyDrop, n: &'static str }
fn checkconsistency(this: &MyDrop) { assert!(this.p==null::<MyDrop>() || this.p==this); }
impl Debug for MyDrop
{ fn fmt(&self, f:&mut Formatter) -> Result<(),Error>
{ checkconsistency(self); write!(f, "MyDrop l: {:p} p: {:p} n: {}",self,self.p,self.n) } }
impl Drop for MyDrop { fn drop(&mut self) { checkconsistency(self); println!("MyDrop::drop self = {:?}",self) } }
pub fn myinit(this: &mut MyDrop, name:&'static str)
{ assert!(this.p==null::<MyDrop>()); // Fail if already initialized.
this.p = this; this.n = name; checkconsistency(this);
println!("myinit this after = {:?}",this); }
// Need an innocuous 'constant' for 2-step initialization.
pub const MYDROPNULL: MyDrop = MyDrop { p: null::<MyDrop>(), n: "from MYDROPNULL" };
}
use crate::no_copy::{MyDrop,MYDROPNULL,myinit};
#[allow(unused_imports)] use std::ptr::null;
use std::mem::{size_of};
fn main()
{ { println!("&MYDROPNULL = {:p}",&MYDROPNULL); // appears to allocate temporary & immediately throw it away
let pmydropconst = &MYDROPNULL;
println!("pmydropconst = {:p} {:?}",pmydropconst,*pmydropconst);
println!("before allocating mydrop...");
// let mut mydrop: MyDrop = MyDrop { p: null::<MyDrop>(), n: "from mydrop" }; // fail: fields p,n are private.
let mut mydrop: MyDrop = MYDROPNULL;
println!("after allocating mydrop...");
myinit(&mut mydrop,"from mydrop init"); }
println!("");
{ let mut bigbuffer = [MYDROPNULL;SBYTES/size_of::<MyDrop>()];
println!("SBYTES = {} size_of::<MyDrop>() = {} bigbuffer.len() = {}",SBYTES,size_of::<MyDrop>(),bigbuffer.len());
let bigbufferslice = &mut bigbuffer[..]; // bigbuffer can't move with slice outstanding!
for i in (0..bigbufferslice.len()).step_by(STEP) { myinit(&mut bigbufferslice[i],"from init loop"); }
println!("bigbufferslice initialized");
bigbufferslice[0]=MYDROPNULL; // Unfortunately, we can't disallow this.
println!("bigbufferslice[0] = {:?}",bigbufferslice[0]);
for i in 0..bigbufferslice.len() { println!("bigbufferslice[{}] = {:?}",i,bigbufferslice[i]) } }
println!("Done.");
}
$ SBYTES=128 cargo run
Compiling mydroparray v0.1.0 (~/rs_projects/mydroparray)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/mydroparray`
&MYDROPNULL = 0x7ffc7d0ce1e0
MyDrop::drop self = MyDrop l: 0x7ffc7d0ce1e0 p: 0x0 n: from MYDROPNULL
pmydropconst = 0x7ffc7d0ce200 MyDrop l: 0x7ffc7d0ce200 p: 0x0 n: from MYDROPNULL
before allocating mydrop...
after allocating mydrop...
myinit this after = MyDrop l: 0x7ffc7d0ce2a0 p: 0x7ffc7d0ce2a0 n: from mydrop init
MyDrop::drop self = MyDrop l: 0x7ffc7d0ce2a0 p: 0x7ffc7d0ce2a0 n: from mydrop init
MyDrop::drop self = MyDrop l: 0x7ffc7d0ce200 p: 0x0 n: from MYDROPNULL
SBYTES = 128 size_of::<MyDrop>() = 24 bigbuffer.len() = 5
myinit this after = MyDrop l: 0x7ffc7d0ce318 p: 0x7ffc7d0ce318 n: from init loop
myinit this after = MyDrop l: 0x7ffc7d0ce348 p: 0x7ffc7d0ce348 n: from init loop
myinit this after = MyDrop l: 0x7ffc7d0ce378 p: 0x7ffc7d0ce378 n: from init loop
bigbufferslice initialized
MyDrop::drop self = MyDrop l: 0x7ffc7d0ce318 p: 0x7ffc7d0ce318 n: from init loop
bigbufferslice[0] = MyDrop l: 0x7ffc7d0ce318 p: 0x0 n: from MYDROPNULL
bigbufferslice[0] = MyDrop l: 0x7ffc7d0ce318 p: 0x0 n: from MYDROPNULL
bigbufferslice[1] = MyDrop l: 0x7ffc7d0ce330 p: 0x0 n: from MYDROPNULL
bigbufferslice[2] = MyDrop l: 0x7ffc7d0ce348 p: 0x7ffc7d0ce348 n: from init loop
bigbufferslice[3] = MyDrop l: 0x7ffc7d0ce360 p: 0x0 n: from MYDROPNULL
bigbufferslice[4] = MyDrop l: 0x7ffc7d0ce378 p: 0x7ffc7d0ce378 n: from init loop
MyDrop::drop self = MyDrop l: 0x7ffc7d0ce318 p: 0x0 n: from MYDROPNULL
MyDrop::drop self = MyDrop l: 0x7ffc7d0ce330 p: 0x0 n: from MYDROPNULL
MyDrop::drop self = MyDrop l: 0x7ffc7d0ce348 p: 0x7ffc7d0ce348 n: from init loop
MyDrop::drop self = MyDrop l: 0x7ffc7d0ce360 p: 0x0 n: from MYDROPNULL
MyDrop::drop self = MyDrop l: 0x7ffc7d0ce378 p: 0x7ffc7d0ce378 n: from init loop
Done.
Comments:
There appears to be no way to properly initialize arrays of Drop objects with only one pass.
We must first initialize the array with an innocuous copyable value, and then initialize each
element again with the proper value.
Unfortunately, this method also allows us to effectively uninitialize the array element by
assigning the innocuous value again.
BTW, Rust allows us to take the address of this supposed 'constant' (!?!), but for some reason
it allocates a new copy and immediately throws it away (!?!).
I don't understand the point of Rust's allowing assignment for !Copy objects; if Rust had
a more rational way to initialize !Copy objects, then assignment wouldn't be necessary.