如何创建自己的类型包装数组类型,以避免复制特性



您知道如何通过包装现有类型来创建新类型,只是因为您想避免Copy特性?!例如,您有bool,并且想要一个新类型MyBool,您可以使用struct MyBool(bool);,然后可以使用它来避免Copy特性。

我想知道的是,如何为数组类型创建一个新类型?例如对于let a = [0; 4];中的a的类型为[{integer}; 4]、四个整数元素的数组。你能为一个特定类型X和特定长度Y的数组做这件事吗?或者,只针对T类型的数组(想想泛型(,并且在新类型中嵌入/不嵌入(?(特定数组长度?

因此,对于bool,以下代码不会显示任何警告/错误(即使您在上面运行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)]
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
}

但我想让它显示错误,因此,我为boolMyBool:使用了一种新的类型

#![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!
}

现在它向我展示了我想要看到的东西:

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

与上面类似,我想为这个数组程序使用新的类型,它不会产生警告或错误(即使通过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!
}

其输出为:

[i32; 4]
()
x(before) = [0, 0, 0, 0]
x(after) = [1, 2, 3, 4]
a = [0, 0, 0, 0]

所以你看,我想要一个新的类型,以避免在调用foo(a);时复制a,这样计算机/编译器就可以通过错误来跟踪我何时会在代码中引入如此容易的错误,而这种错误只有在调用foo(a);时移动a时才会发生(而不仅仅是复制(。

旁注:你认为这是一块短皮棉吗https://github.com/rust-lang/rust-clippy/issues/9061如果实施,在这种情况下是否能够为我发出警告/出错?那太酷了!

就我个人而言,我不喜欢Copy特性,唯一的原因是它绕过了借用检查器,从而允许您编写引入细微错误的代码。即,您可以继续使用";移动";(复制的(变量,编译器不会抱怨。

即使有更好的方法来实现我想要的,也请回答标题问题:如何将数组类型封装在我自己的新类型中?

您可以像包装bool:一样包装数组

struct MyArray ([i32; 4]);

如果你不想为每个数组重新定义不同的包装器类型,你可以使用泛型:

struct MyArray<T, const N: usize> ([T; N]);

我不明白你为什么不想复制你的变量。正在复制的变量不是错误,或者不会创建错误。

点1

在rust中,通过函数修改数据的唯一方法是:

  • 将变量的可变引用传递给函数
  • 将函数的结果赋值回原来的可变值

在您展示的示例中,您的变量一开始甚至没有被定义为可变的,它不可能改变。

点2

Copy特性不会绕过借用检查器,因为借用检查器的唯一目标是:

  • 确保指针始终指向有效数据
  • 确保始终只有一个数据所有者

在您显示的情况下,您不涉及任何指针,而且数据本身是重复的,所以借用检查器不会给出垃圾,它甚至没有涉及。函数中发生的任何事情都将留在函数中。

无论如何

现在,无论出于何种原因,如果您希望借用检查器在这种情况下向您显示错误(同样,没有理由让借用检查器参与其中(,正确的方法可能是将变量的所有权赋予Box这样的专用类型,并使用该框进行操作(框不实现Copy特性(。

以下代码涉及布尔工作

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}");
}

下面的代码涉及一个包装在盒子里的bool不起作用

`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}");
}

现在,我再一次不明白为什么会有人想这么做。这一更改会对性能产生影响,因为您现在需要在执行任何操作之前从堆中获取变量,而不是从堆栈中读取变量。