Reputation: 53
You know how you can create a new type by wrapping an existing type just because you want to avoid the Copy trait?! For example, you have bool
and you want a new type MyBool, you do struct MyBool(bool);
and then you can use that to avoid the Copy trait.
What I want to know is how do you do it(create a new type, that is) for an array type? eg. for the type of a
in let a = [0; 4];
which is [{integer}; 4]
, array of four integer elements. And can you do it for an array of a specific type X and of a specific len Y ? or, only for an array of type T (think generics) and with/without(?) a specific array length embedded in the new type ?
So, for bool
, the following code doesn't show any warnings/errors(even if you run cargo clippy
on it):
#![deny(clippy::all, clippy::pedantic, clippy::nursery, warnings, future_incompatible,
nonstandard_style, non_ascii_idents, clippy::restriction, rust_2018_compatibility,
rust_2021_compatibility, unused)]
#![allow(clippy::print_stdout, clippy::use_debug, clippy::missing_docs_in_private_items)]
#![allow(clippy::blanket_clippy_restriction_lints)] //workaround clippy
// might want to deny later:
//#![allow(clippy::default_numeric_fallback)] // might want to deny later!
//#![allow(clippy::dbg_macro)]
fn main() {
let mut has_spawned:bool=false;
//...
let handler=std::thread::spawn(move || {
println!("Before {has_spawned}!"); // false
has_spawned=true;
println!("Set {has_spawned}!"); // true
});
#[allow(clippy::unwrap_used)]
handler.join().unwrap();
println!("Current {has_spawned}!"); // false
}
But I want it to show me errors, thus, I use a new type for bool
, MyBool
:
#![deny(clippy::all, clippy::pedantic, clippy::nursery, warnings, future_incompatible,
nonstandard_style, non_ascii_idents, clippy::restriction, rust_2018_compatibility,
rust_2021_compatibility, unused)]
#![allow(clippy::print_stdout, clippy::use_debug, clippy::missing_docs_in_private_items)]
#![allow(clippy::blanket_clippy_restriction_lints)] //workaround clippy
#[derive(Debug)]
struct MyBool(bool);
fn main() {
let mut my_has_spawned:MyBool=MyBool(false);
//...
let handler=std::thread::spawn(move || {
println!("Before {my_has_spawned:?}!"); //MyBool(false)
//my_has_spawned=MyBool(true);
my_has_spawned.0=true;
println!("Set {my_has_spawned:?}!"); // MyBool(true)
});
#[allow(clippy::unwrap_used)]
handler.join().unwrap();
println!("Current {my_has_spawned:#?}!"); // value borrowed here after move, XXX: this is what
// I wanted!
}
and now it shows me exactly what I want to see:
error[E0382]: borrow of moved value: `my_has_spawned`
--> /home/user/sandbox/rust/copy_trait/gotcha1/copy_trait_thread_newtype/src/main.rs:20:24
|
10 | let mut my_has_spawned:MyBool=MyBool(false);
| ------------------ move occurs because `my_has_spawned` has type `MyBool`, which does not implement the `Copy` trait
11 | //...
12 | let handler=std::thread::spawn(move || {
| ------- value moved into closure here
13 | println!("Before {my_has_spawned:?}!"); //MyBool(false)
| -------------- variable moved due to use in closure
...
20 | println!("Current {my_has_spawned:#?}!"); // value borrowed here after move, XXX: this is what
| -------------------^^^^^^^^^^^^^^-------
| | |
| | value borrowed here after move
| in this macro invocation (#1)
|
::: /usr/lib/rust/1.64.0/lib/rustlib/src/rust/library/std/src/macros.rs:101:1
|
101 | macro_rules! println {
| -------------------- in this expansion of `println!` (#1)
...
106 | $crate::io::_print($crate::format_args_nl!($($arg)*));
| --------------------------------- in this macro invocation (#2)
|
::: /usr/lib/rust/1.64.0/lib/rustlib/src/rust/library/core/src/macros/mod.rs:906:5
|
906 | macro_rules! format_args_nl {
| --------------------------- in this expansion of `$crate::format_args_nl!` (#2)
For more information about this error, try `rustc --explain E0382`.
error: could not compile `copy_trait_thread_newtype` due to previous error
Similarly to the above, I want to use the new type for this array program that yields no warnings or errors(even through cargo clippy
):
#![deny(clippy::all, clippy::pedantic, clippy::nursery, warnings, future_incompatible,
nonstandard_style, non_ascii_idents, clippy::restriction, rust_2018_compatibility,
rust_2021_compatibility, unused)]
#![allow(clippy::print_stdout, clippy::use_debug, clippy::missing_docs_in_private_items)]
#![allow(clippy::blanket_clippy_restriction_lints)] //workaround clippy
// might want to deny later:
#![allow(clippy::default_numeric_fallback)] // might want to deny later!
#![allow(clippy::dbg_macro)]
//src: https://users.rust-lang.org/t/rust-book-suggestion-add-a-section-regarding-copy-vs-move/1549/2
fn foo(mut x: [i32; 4]) {
println!("x(before) = {:?}", x);
x = [1, 2, 3, 4];
println!("x(after) = {:?}", x);
}
//src: https://stackoverflow.com/a/58119924/19999437
fn print_type_of<T>(_: &T) {
//println!("{}", std::any::type_name::<T>());
println!("{}", core::any::type_name::<T>());
}
//struct MyArray(array); //TODO: how?
fn main() {
let a = [0; 4];
//a.something();//method not found in `[{integer}; 4]`
//a=1;//so this is an array
//dbg!(a);
println!("{:#?}", print_type_of(&a)); // i32
foo(a); //sneakily copied! thanks Copy trait!
println!("a = {:?}", a);//unchanged, doh! but since it was just copied above, can use it here
//without errors!
}
output of that is:
[i32; 4]
()
x(before) = [0, 0, 0, 0]
x(after) = [1, 2, 3, 4]
a = [0, 0, 0, 0]
So you see, I want a new type in order to avoid the copying of a
when foo(a);
is called, so that the computer/compiler can keep track of when I would introduce such easy bugs in my code, by error-ing instead, which happens only when a
is moved(instead of just copied) when foo(a);
is called.
Side note: do you think that this clippy lint https://github.com/rust-lang/rust-clippy/issues/9061 if implemented would be able to warn/err for me in such cases? That would be cool!
Personally I dislike the Copy trait for the only reason that it bypasses the borrow checker and thus allows you to write code that introduces subtle bugs. ie. you can keep using the stale value of the "moved"(copied) variable and the compiler won't complain.
Even if there are better ways to do what I want, please also do answer the title question: how I can wrap the array type in my own new type?
Upvotes: 0
Views: 527
Reputation: 23329
You can wrap an array the same way you would wrap a bool
:
struct MyArray ([i32; 4]);
And you can use generics if you don't want to redefine different wrapper types for each array:
struct MyArray<T, const N: usize> ([T; N]);
Upvotes: 2
Reputation: 118
I don't understand why you don't want your variable to be copied. Your variable being copied isn't a bug or won't create bugs.
The only way in rust to modify data via a function is either :
In the example you show, your variable isn't even defined as mutable in the first place, there is no way it would change.
The Copy
trait doesn't bypass the borrow checker, as the borrow checker only goal is :
In the case you show, you don't involve any pointer, and the data itself is duplicated, so the borrow checker won't give a crap, it isn't even involved. Whatever happens in the function stays in the function.
Now if you want, for whatever reason, the borrow checker to show you errors in this situation (once again, there is no reason for the borrow checker to be involved), the proper way would probably be to give the ownership of your variable to a dedicated type like a Box
, and use the box for your operations (box don't implement the Copy
trait).
The following code involving a bool works
fn main() {
let a = false;
effect(a);
println!("main function : {a}");
}
fn effect(mut a: bool) {
println!("effect before : {a}");
a = !a;
println!("effect after : {a}");
}
The following code involving a bool wrapped inside a box doesn't work
`fn main() {
let a = Box::from(false);
effect(a);
println!("main function : {a}"); // error
}
fn effect(mut a: Box<bool>) {
println!("effect before : {a}");
*a = !*a;
println!("effect after : {a}");
}
Now, once again, I don't understand why anyone would want to do this. This change has a performance impact, as you now need to take your variable from the heap before any operation, instead if just reading it from the stack.
Upvotes: 0