I have a code that looks approximately like this:
// src/bitboard.rs
#[derive(Copy, Clone, Debug)]
pub struct Bitboard {
value: u64
}
impl Bitboard {
pub const fn new(value: u64) -> Self {
Self { value }
}
pub const fn get_bit(&self, k: u64) -> u64 {
((1u64 << k) & self.value) >> k
}
}
// src/main.rs
pub mod bitboard;
use crate::bitboard::Bitboard;
fn main() {
let bitboard = Bitboard::new(0);
dbg!(bitboard);
}
If I compile it exactly like this, it works without any errors or warnings.
However, if I change my pub mod bitboard to mod bitboard, then clippy starts giving me this warning:
warning: this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
pub const fn get_bit(&self, k: u64) -> u64 {
^^^^^ help: consider passing by value instead: `self`
= note: `-W clippy::trivially-copy-pass-by-ref` implied by `-W clippy::pedantic`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
I understand what clippy is telling me to do. But I do not understand why it does not suggest this when my module is declared as public. Any ideas?
P.S.: I am using rustc 1.60.0, clippy 0.1.60.
Edit: I should also add that this is not the only extra lint that happens when I remove the pub from my modules. For instance, I also had 1 new clippy::upper_case_acronyms, and 1 new clippy::enum_variant_names.
Edit 2: As requested, I'm including more examples to show the same behaviour happening to clippy::upper-case_acronyms and clippy::enum_variant_names:
// src/fen.rs
pub struct FEN; // upper case acronyms happens here
pub enum FENValidationError { // enum variant names happens here
InvalidFieldsCount,
InvalidPiecePlacement,
InvalidRankSize,
}
// src/main.rs
mod fen; // same thing here. If this becomes `pub mod fen`, the two lint warnings above disappear.
Because changing a public API like that is a breaking change.
Changing the argument to pass by value like clippy recommends is easy if its an internal method, since you have control over all of the code that uses it. But if the argument is on a function or method exposed as part of the public API, changing it would require that all the other projects that use it change too, which is usually unacceptable for something as minor as passing a small Copy value by reference.
Related
I'm working on a Rust project. I'm using Cargo feature flags for conditional compilation of the some code. There are cases where I have to include the entire file in the feature flags so doing so adding #[cfg(feature="my-flag")] over every function & use statement doesn't make much sense.
So to include the entire file in the feature flag I'm thinking to surround all the contents in the file a block & add the feature flag for the block.
#[cfg(feature="my-flag")]
{
use crate::access_control::{func1, func2};
use crate::models;
...
#[derive(Debug)]
pub enum MyEnum{..}
#[derive(Clone)]
pub Struct MyStruct{..}
pub fn my_func() {...}
fn my_func_internal() {...}
...
}
But I'm getting the error Syntax Error: expected an item after attributes
Also, there are also some cases where I want the entire directory to be included the feature flags. How should I go about it? Doing the adding feature flags for every file is one way. Does a better way exist?
As in #MarcusDunn's answer, the proper solution is to apply the attribute to the mod declaration:
// This conditionally includes a module which implements WEBP support.
#[cfg(feature = "webp")]
pub mod webp;
However for the sake of completeness, I would point out that attributes can be applied to the item they're in instead of being applied to the following item. These are called "inner attributes" and are specified by adding a ! after the #:
#![cfg(feature="my-flag")] // Applies to the whole file
use crate::access_control::{func1, func2};
use crate::models;
#[derive(Debug)]
pub enum MyEnum {}
#[derive(Clone)]
pub struct MyStruct {}
pub fn my_func() {}
fn my_func_internal() {}
From https://doc.rust-lang.org/cargo/reference/features.html
// This conditionally includes a module which implements WEBP support.
#[cfg(feature = "webp")]
pub mod webp;
This could be an entire directory - or a single file, depends how you structure your modules.
You can use conditional compilation on modules. Perhaps something like this would work for your use case:
#[cfg(feature = "feat")]
use feat::S;
#[cfg(not(feature = "feat"))]
use no_feat::S;
mod feat {
pub const S: &str = "feat";
}
mod no_feat {
pub const S: &str = "no_feat";
}
fn main() {
println!("{}", S);
}
Running with cargo run:
no_feat
Running with cargo run --features feat:
feat
You can use the cfg-if crate:
cfg_if::cfg_if! {
if #[cfg(feature = "my-flag")] {
// ...
}
}
While working with a HashMap that uses &'static str as the key type, I created a newtype to hash by the pointer rather than by the string contents to reduce overhead.
pub struct StaticStr(&'static str);
impl Hash for StaticStr {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.as_ptr().hash(state)
}
}
impl PartialEq for StaticStr {
fn eq(&self, other: &Self) -> bool {
self.0.as_ptr() == other.0.as_ptr()
}
}
impl Eq for StaticStr {}
It turns out that this does not work consistently, as in the following example.
pub type MyMap = HashMap<StaticStr, u8>;
pub const A: &str = "A";
pub fn make_map() -> MyMap {
let mut map = MyMap::new();
map.insert(StaticStr(A), 1);
map
}
pub fn get_value(control: &MyMap) -> Option<u8> {
control.get(&StaticStr(A)).cloned()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn map_made_in_lib() {
let map = make_map();
assert_eq!(get_value(&map), Some(1));
}
#[test]
pub fn map_made_in_test() {
// Same as make_map()
let mut map = MyMap::new();
map.insert(StaticStr(A), 1);
// This check fails
assert_eq!(get_value(&map), Some(1));
}
}
Notice that in the first test, the string constant A is only used directly in the lib crate. In the second test, A is used directly in both the lib crate and the test crate. I discovered that although both tests use the same string constant, the pointers are different depending on which crate refers to the string constant by name. This is demonstrated in the minimal reproduction I created. I would have expected that the string literal be included only once for the crate that defines it, or at least that the linker would be smart enough to deduplicate the string literals. Is there a reason for this behavior?
Instead of a const try a static?
A constant item is an optionally named constant value which is not
associated with a specific memory location in the program. Constants
are essentially inlined wherever they are used, meaning that they are
copied directly into the relevant context when used. This includes
usage of constants from external crates, and non-Copy types.
References to the same constant are not necessarily guaranteed to
refer to the same memory address. -- The Rust Reference
A static item is similar to a constant, except that it represents a
precise memory location in the program. All references to the static
refer to the same memory location. Static items have the static
lifetime, which outlives all other lifetimes in a Rust program. Static
items do not call drop at the end of the program. -- The Rust Reference
I need a static array of structs and the structs contain a Vec. I can manage the lifetimes of the actual values. I get the following error:
: Mar23 ; cargo test
Compiling smalltalk v0.1.0 (/Users/dmason/git/AST-Smalltalk/rust)
error[E0507]: cannot move out of `dispatchTable[_]` as `dispatchTable` is a static item
--> src/minimal.rs:32:44
|
30 | let old = ManuallyDrop::into_inner(dispatchTable[pos]);
| ^^^^^^^^^^^^^^^^^^ move occurs because `dispatchTable[_]` has type `ManuallyDrop<Option<Box<Dispatch>>>`, which does not implement the `Copy` trait
error: aborting due to previous error
Here is a minimal compilable example:
#[derive(Copy, Clone)]
struct MethodMatch {
hash: i64,
method: Option<bool>,
}
#[derive(Clone)]
pub struct Dispatch {
class: i64,
table: Vec<MethodMatch>,
}
const max_classes : usize = 100;
use std::mem::ManuallyDrop;
const no_dispatch : ManuallyDrop<Option<Box<Dispatch>>> = ManuallyDrop::new(None);
static mut dispatchTable : [ManuallyDrop<Option<Box<Dispatch>>>;max_classes] = [no_dispatch;max_classes];
use std::sync::RwLock;
lazy_static! {
static ref dispatchFree : RwLock<usize> = {RwLock::new(0)};
}
pub fn addClass(c : i64, n : usize) {
let mut index = dispatchFree.write().unwrap();
let pos = *index;
*index += 1;
replaceDispatch(pos,c,n);
}
pub fn replaceDispatch(pos : usize, c : i64, n : usize) -> Option<Box<Dispatch>> {
let mut table = Vec::with_capacity(n);
table.resize(n,MethodMatch{hash:0,method:None});
unsafe {
let old = ManuallyDrop::into_inner(dispatchTable[pos]);
dispatchTable[pos]=ManuallyDrop::new(Some(Box::new(Dispatch{class:c,table:table})));
old
}
}
The idea I had was to have replaceDispatch create a new Dispatch option object, and replace the current value in the array with the new one, returning the original, with the idea that the caller will get the Dispatch option value and be able to use and then drop/deallocate the object.
I found that it will compile if I add .clone() right after the identified error point. But then the original value never gets dropped, so (the into_inner is redundant and) I'm creating a memory leak!. Do I have to manually drop it (if I could figure out how)? I thought that's what ManuallyDrop bought me. In theory, if I created a copy of the fields from the Vec into a copy, that would point to the old data, so when that object got dropped, the memory would get freed. But (a) that seems very dirty, (b) it's a bit of ugly, unnecessary code (I have to handle the Some/None cases, look inside the Vec, etc.), and (c) I can't see how I'd even do it!!!!
As the compiler tells you, you cannot move a value out of a place observable by others. But since you have the replacement at the ready, you can use std::mem::replace:
pub fn replaceDispatch(pos: usize, c: i64, n: usize) -> Option<Box<Dispatch>> {
... table handling omitted ...
unsafe {
let old = std::mem::replace(
&mut dispatchTable[pos],
ManuallyDrop::new(Some(Box::new(Dispatch {
class: c,
table: table,
}))),
);
ManuallyDrop::into_inner(old)
}
}
Playground
In fact, since you're using the Option to manage the lifetime of Dispatch, you don't need ManuallyDrop at all, and you also don't need the Box: playground.
I'm trying to implement the Add trait for anything that implements another trait (in the example code the Test trait). I'm using references in the Add implementation because not everything that implements Test will be the same size. The code below compiles fine:
use std::ops::Add;
struct Foo(i32);
struct Bar(i64);
trait Test {}
impl Test for Foo {}
impl Test for Bar {}
impl<'a, 'b> Add<&'b Test> for &'a Test {
type Output = Box<Test>;
fn add(self, other: &'b Test) -> Box<Test> {
if true {
Box::new(Foo(5))
} else {
Box::new(Bar(5))
}
}
}
When I try to actually use Add, as below, it says that the operation could not be applied because the implementation of Add for &Foo is missing.
fn test_add() {
&Foo(5) + &Bar(5)
}
Have I defined the implementation incorrectly? Have I called it incorrectly? The goal is to make the function add take two references to objects which both implement Test, and return a reference (or box) to a new object that implements Test (and might not be the same underlying type as either of the inputs).
I found another approach that changes the behavior slightly, but works.
struct V<T>(T);
use std::ops::Add;
impl<T1: Test, T2: Test> Add<V<Box<T2>>> for V<Box<T1>> {
type Output = V<Box<Test>>;
fn add(self, other: V<Box<T2>>) -> Self::Output {
unimplemented!()
}
}
That allows it to return any type that implements Test, at the cost of wrapping everything in a Box and a dummy struct V. Not very elegant, and I still don't understand why my original code doesn't work, but at least this has the behavior I wanted.
The problem is that the compiler cannot implicitly convert from &Foo into &Test. If you explicitly convert it into &Test first, then the operator overloading works:
fn test_add() {
let foo: &Test = &Foo(5);
foo + &Bar(5);
}
Alternatively, you can use the fully qualified syntax:
fn test_add() {
<&Test as Add<&Test>>::add(&Foo(5), &Bar(5));
}
I'm new to Rust and have seen some examples of people using Box to allow pushing many types that implement a certain Trait onto a Vec. When using a Trait with Generics, I have run into an issue.
error[E0038]: the trait `collision::collision_detection::Collidable` cannot be made into an object
--> src/collision/collision_detection.rs:19:5
|
19 | collidables: Vec<Box<Collidable<P, M>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `collision::collision_detection::Collidable` cannot be made into an object
|
= note: method `get_ncollide_shape` has generic type parameters
error: aborting due to previous error
error: Could not compile `game_proto`.
To learn more, run the command again with --verbose.
Here is my code
extern crate ncollide;
extern crate nalgebra as na;
use self::ncollide::shape::Shape;
use self::ncollide::math::Point;
use self::ncollide::math::Isometry;
use self::na::Isometry2;
pub trait Collidable<P: Point, M> {
fn get_ncollide_shape<T: Shape<P, M>>(&self) -> Box<T>;
fn get_isometry(&self) -> Isometry2<f64>;
}
pub struct CollisionRegistry<P, M>
where
P: Point,
M: Isometry<P>,
{
collidables: Vec<Box<Collidable<P, M>>>,
}
impl<P: Point, M: Isometry<P>> CollisionRegistry<P, M> {
pub fn new() -> Self {
let objs: Vec<Box<Collidable<P, M>>> = Vec::new();
CollisionRegistry { collidables: objs }
}
pub fn register<D>(&mut self, obj: Box<D>)
where
D: Collidable<P, M>,
{
self.collidables.push(obj);
}
}
I'm trying to use collidables as a list of heterogenous game objects that will give me ncollide compatible Shapes back to feed into the collision detection engine.
EDIT:
To clear up some confusion. I'm not trying to construct and return an instance of a Trait. I'm just trying to create a Vec that will allow any instance of the Collidable trait to be pushed onto it.
Rust is a compiled language, so when it compiles your code, it needs to know all of the information it might need to generate machine code.
When you say
trait MyTrait {
fn do_thing() -> Box<u32>;
}
struct Foo {
field: Box<MyTrait>
}
you are telling Rust that Foo will contain a box containing anything implementing MyTrait. By boxing the type, the compiler will erase any additional data about the data type that isn't covered by the trait. These trait objects are implemented as a set of data fields and a table of functions (called a vtable) that contains the functions exposed by the trait, so they can be called.
When you change
fn do_thing() -> Box<u32>;
to
fn do_thing<T>() -> Box<T>;
it may look similar, but the behavior is much different. Let's take a normal function example
fn do_thing<T>(val: T) { }
fn main() {
do_thing(true);
do_thing(45 as u32);
}
the compiler performs what is a called monomorphization, which means your code in the compiler becomes essentially
fn do_thing_bool(val: bool) { }
fn do_thing_num(val: u32) { }
fn main() {
do_thing_bool(true);
do_thing_num(45 as u32);
}
The key thing to realize is that you are asking it to do the same thing for your trait. The problem is that the compiler can't do it. The example above relies on knowing ahead of time that do_thing is called with a number in one case and a boolean in another, and it can know with 100% certainty that those are the only two ways the function is used.
With your code
trait MyTrait {
fn do_thing<T>() -> Box<T>;
}
the compiler does not know what types do_thing will be called with, so it has no way to generate functions you'd need to call. To do that, wherever you convert the struct implementing Collidable into a boxed object it would have to know every possible return type get_ncollide_shape could have, and that is not supported.
Other links for this:
Understanding Traits and Object Safety
https://www.reddit.com/r/rust/comments/3an132/how_to_wrap_a_trait_object_that_has_generic/