mod math {
#[derive(Debug)]
pub struct SomeLevel {
pub value: f64,
pub priority: u8,
pub color: String
}
impl fmt::Display for SomeLevel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "value: {}, priority: {}, color: {}", self.value, self.priority, self.color)
}
}
pub static some_levels: &'static Vec<&'static SomeLevel> = &[
SomeLevel { value: 0.0, priority: 1, color: "".to_string()},
];
}
The error message I am getting is:
^ expected struct `Vec`, found array of 21 elements
I was doing vec![...] before, but I decided I want this as a static or constant collection.
Can someone help and explain what the issue is?
Your first attempt:
pub static SOME_LEVELS: &'static Vec<&'static SomeLevel> = &[
SomeLevel { value: 0.0, priority: 1, color: "".to_string()},
];
fails because a Vec is a completely different type to a static array in rust. A Vec is a dynamically growable heap allocated Vector type, while an array is a fixed size array, typically allocated on the stack.
You can correct this by altering some_levels to be an array (assuming that you don't actually need the facilities of a Vec):
pub static SOME_LEVELS: &[SomeLevel; 1] = &[
SomeLevel { value: 0.0, priority: 1, color: "".to_string()},
];
This still does not compile though:
error[E0015]: cannot call non-const fn `<str as ToString>::to_string` in statics
--> src/lib.rs:18:60
|
18 | SomeLevel { value: 0.0, priority: 1, color: "".to_string()},
| ^^^^^^^^^^^
|
= note: calls in statics are limited to constant functions, tuple structs and tuple variants
The reason for this error is that functions used to initialize a static need to be const, and to_string() is not const. It is possible to create an empty string using String::new(), which is a const function, but I am assuming that your real life lookup table would not contain only empty strings.
This leaves you with a couple of choices. If SomeLevel::color does not really need to be a String - for example if it is actually only ever assigned to fixed string literals - then you can change it to be &'static str:
#[derive(Debug)]
pub struct SomeLevel {
pub value: f64,
pub priority: u8,
pub color: &'static str
}
// ....
pub static SOME_LEVELS: &[SomeLevel; 1] = &[
SomeLevel { value: 0.0, priority: 1, color: ""},
];
Note that statics in rust are always immutable, so if this is the only use case of SomeLevel then most likely &'static str is what you want. If you really did need to have a String, then you would have to use lazy initialization of your static table, like this:
use once_cell::sync::Lazy;
// ...
pub static SOME_LEVELS: Lazy<[SomeLevel; 1]> = Lazy::new(|| [
SomeLevel { value: 0.0, priority: 1, color: "".to_string() },
]);
Here we use once_cell::sync::Lazy to lazily initialize SOME_LEVELS the first time it is referenced. It is initialized within a closure called by Lazy::new, which also means that the intialization value must be returned by value rather than reference.
Note: Lazy is also available in the standard library, under the name LazyCell, however it is not stabilized yet. The once_cell crate can be used in the meanwhile. lazy_static is another crate that provides similar functionality.
Related
I have a struct that has 20 fields:
struct StructA {
value1: i32,
value2: i32,
// ...
value19: i32,
day: chrono::NaiveDate,
}
I'd like to impl Default trait for StructA. I tried to add #[derive(Default)] to the struct, but chrono::NaiveDate doesn't implement Default.
I then tried to implement Default for StructA:
impl Default for StructA {
fn default() -> Self {
Self {
value1: Default::default(),
value2: Default::default(),
// ...
value19: Default::default(),
day: chrono::NaiveDate::from_ymd(2021, 1, 1),
}
}
}
This code works fine, but the parts of value1 through value19 are redundant. Is there a solution with less code?
I defined StructA to deserialize JSON data via serde-json so I can't change the struct's definition.
A value of day: chrono::NaiveDate is always given from JSON data, so I want to avoid day: Option<chrono::NaiveDate>.
The derivative crate makes this kind of thing a breeze:
#[derive(Derivative)]
#[derivative(Default)]
struct StructA {
value1: i32,
value2: i32,
// ...
value19: i32,
#[derivative(Default(value = "NaiveDate::from_ymd(2021, 1, 1)"))]
day: NaiveDate,
}
If you want to avoid external crates, your options are:
the approach you already used, with the downside that you must name all the fields. Also note that you don't need to repeat Default::default() for each numeric field, a simple 0 will work as well.
make day an Option and derive Default, with the downside that it will default to None, bear a run-time cost, and you'll have to unwrap() to access it.
make day a newtype that wraps NaiveDate and implements Default to set it to the desired value, with the downside that you'll need to access the NaiveDate through a (zero-cost) field or method.
That's a rather dirty trick, but you can wrap your date in an Option, and it has an implementation of Default. Then you won't need to implement Default on your own, you can derive it. To keep the same semantics of StructA::default() you'll need to write your own method (luckily Rust allows to define default() method besides already derived Default::default()) Playground
use chrono;
#[derive(Debug, Default)]
struct StructA {
value1: i32,
value2: i32,
value19: i32,
day: Option<chrono::NaiveDate>,
}
impl StructA {
fn default() -> Self {
let mut instance: Self = Default::default();
instance.day = Some(chrono::NaiveDate::from_ymd(2021, 1, 1));
instance
}
}
fn main() {
println!("{:?}", StructA::default());
// StructA { value1: 0, value2: 0, value19: 0, day: Some(2021-01-01) }
}
Downsides of this version:
Need to .unwrap() the date everywhere it's used
Two methods with same name default, but one is Self::default which fills the date as I implemented and the other is Default::default which fills the date with None, you'll need to be careful which you call (calling StructA::default() invokes Self::default())
EDIT. Please be careful with this answer (details in the comments by #user4815162342)
In short - the last downside of having two different .default() methods in one type is dangerous in generic methods with T: Default arguments, because in this case will be called Default::default(), which initializes the day field to None. The worst part of this effect, is that compiler won't ever warn you about it, thus forcing you to spend your time debugging in case of a bug.
There's one similar approach suggested by #Ă–merErden, where you can again wrap the date into another type, to which you implement Default on your own. This will ensure that your field will always be initialized, but still forces you to somehow "unwrap" the value. In case of wrapping NaiveDate into a tuple struct, you can unwrap as simply as instance.day.0 or implement Deref to this wrapper and unwrap with *instance.day
use chrono;
use std::ops::Deref;
#[derive(Debug)]
struct NaiveDateWrapper(chrono::NaiveDate);
impl Default for NaiveDateWrapper {
fn default() -> Self {
Self(chrono::NaiveDate::from_ymd(2021, 1, 1))
}
}
impl Deref for NaiveDateWrapper {
type Target = chrono::NaiveDate;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Default)]
struct StructA {
value1: i32,
value2: i32,
value19: i32,
day: NaiveDateWrapper,
}
fn main() {
let v = StructA::default();
println!("{:?}", v.day.0);
println!("{:?}", *v.day);
}
There is another crate called Educe https://docs.rs/educe/latest/educe/#default
Some code snippets.
#[macro_use] extern crate educe;
#[derive(Educe)]
#[educe(Default)]
struct Struct {
#[educe(Default = 1)]
f1: u8,
#[educe(Default = 11111111111111111111111111111)]
f2: i128,
#[educe(Default = 1.1)]
f3: f64,
#[educe(Default = true)]
f4: bool,
#[educe(Default = "Hi")]
f5: &'static str,
#[educe(Default = "Hello")]
f6: String,
#[educe(Default = 'M')]
f7: char,
}
#[derive(Educe)]
#[educe(Default)]
enum Enum {
Unit,
#[educe(Default)]
Tuple(
#[educe(Default(expression = "0 + 1"))]
u8,
#[educe(Default(expression = "-11111111111111111111111111111 * -1"))]
i128,
#[educe(Default(expression = "1.0 + 0.1"))]
f64,
#[educe(Default(expression = "!false"))]
bool,
#[educe(Default(expression = "\"Hi\""))]
&'static str,
#[educe(Default(expression = "String::from(\"Hello\")"))]
String,
#[educe(Default(expression = "'M'"))]
char,
),
}
#[derive(Educe)]
#[educe(Default)]
union Union {
f1: u8,
f2: i128,
f3: f64,
f4: bool,
#[educe(Default = "Hi")]
f5: &'static str,
f6: char,
}
In my project I'm frequently iterating through a vector of structs to find an object by some field value, then use some trait function on that object:
pub struct Person{
name: String,
age: u32,
id: u32,
}
impl Person{
pub fn new(name: String, id_num: u32, age: u32)->Self{
let p = Person{
name: name,
id: id_num,
age: age,
};
p
}
}
trait PersonTrait{
fn printname();
fn get_name()->String;
fn get_age()->u32;
fn set_age(age: u32);
}
impl PersonTrait for Person{
fn printname(){
dbg!(self.name)
}
fn get_name()->String{
self.name
}
fn get_id()->u32{
self.id;
}
fn set_age(age: u32){
self.age = age;
}
}
fn main(){
let my_people = vec![Person::new("Rakim".to_string(), 1, 56), Person::new("Ghostface".to_string(), 2, 56), Person::new("RZA".to_string(), 3, 56)];
//frequently repeating this pattern of finding struct in array of structs, then doing something to that found struct
for person in my_people.clone(){
if person.get_id() == 1 {
person.set_age(100);
}
}
for person in my_people.clone(){
if person.get_id() == "Rakim".to_string(){
person.printname();
}
}
}
So the general pattern im using here is:
for x in my_objects{
if x.id() == some_id{
x.do_some_trait_function()
}
}
I'd like to create a more general function to make this syntax simpler, something like:
//not sure what the correct syntax would be here, or how you might pass a trait function as an argument
fn find_then_do_trait_function(obj_list: Vec<Person>, id: u32, trait_function: my_trait_function()){
for x in obj_list(){
if x.get_id() == id {
//use my trait function on x
}
}
}
How might I do this? I know I could create an enum for every trait function, then match that enum, but that also seems pretty verbose.
There's nothing unique about trait functions. You've identified a very common pattern which can be split into two pieces: we want to filter a vector and then perform some operation on each matching element. We can define a function that takes two closure arguments to do this for us.
fn search_and_call<T>(obj_list: &mut Vec<T>,
mut condition: impl FnMut(&mut T) -> bool,
mut func: impl FnMut(&mut T) -> ()) {
for x in obj_list {
if condition(x) {
func(x);
}
}
}
func can be any closure. That closure might call a trait function, or it might print to the screen, or do any number of things. The writer of the above function needn't care; it's all the same as far as we're concerned. Sample usage:
let mut v = vec!(1, 2, 3, 4);
search_and_call(&mut v, |x| *x % 2 == 0, |x| println!("{}", *x));
It's worth noting, however, that Rust's excellent Iterator trait defines a ton of useful functions, and we can get this behavior for free, without even touching a for loop.
let mut v = vec!(1, 2, 3, 4);
v.iter().filter(|x| *x % 2 == 0).for_each(|x| println!("{}", *x));
.iter() gets an iterator over our vector, .filter(...) produces a new iterator that selects certain elements based on our condition, and .for_each(...) calls a function on all elements which remain after the filter.
Since it doesn't know the concrete type of the data, it only contains a vtpr of dyn Trait, How does it drop itself when it goes out of scope? Does every virtual table in Rust contains a drop method implementation?
When the concrete type the original Box contained is unsized into a trait object, the Drop implementation for the type goes into the vtable. A pointer (Any pointer-like thing in Rust. IE, a reference, Box, raw pointer, etc.) whose pointee is a trait object is laid out as follows in memory*:
struct FooTraitDynPointer {
ptr: *[const/mut] (),
vtable: &'static VTableImplForFooTrait
}
The ptr field in my example points to the actual data. We could say that's the original Box.
The vtable field in my example points to a static vtable. Say we have the following Foo trait:
trait Foo {
fn bar(&self) -> usize;
}
Our vtable will look as follows*:
struct VTableImplForFooTrait {
dropper: unsafe fn(*mut ()),
size: usize,
align: usize,
bar: unsafe fn(*const ()) -> usize,
}
We see there, that the drop is there. Along with it, there're size and align fields which allow owning types to deallocate enough memory. Or re-allocate enough memory.
Here's an example program which crudely extracts the size of a struct from within a pointer to a trait object:
#![feature(raw)]
trait Foo {
fn bar(&self) -> usize;
}
struct Baz {
field: f64
}
impl Foo for Baz {
fn bar(&self) -> usize {
self.field as usize
}
}
#[derive(Clone)]
struct FooVTable {
dropper: unsafe fn(*mut ()),
size: usize,
align: usize,
bar: unsafe fn(*const ()) -> usize,
}
fn main() {
use std::{mem, raw};
let value = Baz { field: 20.0 };
let boxed = Box::new(value) as Box<dyn Foo>;
let deconstructed: raw::TraitObject = unsafe { mem::transmute(boxed) };
let vtable = deconstructed.vtable as *mut FooVTable;
let vtable = unsafe { (*vtable).clone() };
println!("size: {}, align: {}", vtable.size, vtable.align);
let result = unsafe { (vtable.bar)(deconstructed.data) };
println!("Value: {}", result);
}
Playground
(Currently) prints:
size: 8, align: 8
Value: 20
However this may very well change in the future so I'm leaving this timestamp here for someone who reads this in a future where the behaviour has been changed. June 5, 2020.
*: The layout of trait objects, and especially their vtables is NOT guaranteed, so do not rely in actual code.
pub struct Character {
name: String,
hp: i32,
level: i32,
xp: i32,
xp_needed: i32,
gold: i32
}
impl Character {
pub fn new(name: String) -> Character {
let mut rng = thread_rng();
let hp: i32 = rng.gen_range(12, 75);
let gold: i32 = rng.gen_range(10, 50);
Character { name: name, hp: hp, level: 1, xp: 0, gold: gold, xp_needed: 100 }
}
pub fn get_name(&self) -> String {
self.name
}
// ...
}
How exactly am I breaking the rules here?
At a high-level, this is against-the-grain for Rust. You cannot transfer ownership of something borrowed because you don't own it.
Um don't I? I have other functions like:
pub fn get_hp(&self) -> i32 {
self.hp
}
And that works just fine.
|
23 | self.name
| ^^^^ cannot move out of borrowed content
error: aborting due to previous error
What's going on? What is the appropriate approach to return the character name? Why does the get_hp method work but not get_name?
The difference between get_hp and get_name is that get_hp returns a i32. i32 is a Copy type. Copy types can be copied by simply copying bits and are never moved out. On the other hand String is not Copy, it manages some memory which must either be transferred (moved out) or Cloned.
For getters like this, it is more idiomatic to return references instead of cloning. And for Strings, it should specifically be &str.
pub fn get_name(&self) -> &str {
&self.name
}
This is what my code looks like. I'm trying to use a impled struct within my ShapeRenderer struct and use its methods.
shapes.rs:
use super::core::*;
pub struct ShapeRenderer<'a> {
core_renderer: &'a mut CanvasRenderer,
}
core.rs
pub struct Canvas {
pub width: usize,
pub height: usize,
pub array: Vec<char>,
}
pub struct Point {
pub x: usize,
pub y: usize,
}
pub struct CanvasRenderer<'a> {
canvas: &'a mut Canvas,
}
impl<'a> CanvasRenderer<'a> {
pub fn new(canvas: &'a mut Canvas) -> CanvasRenderer {
CanvasRenderer { canvas: canvas }
}
}
Error
error[E0107]: wrong number of lifetime parameters: expected 1, found 0
--> src/shapes.rs:5:28
|
5 | core_renderer: &'a mut CanvasRenderer
| ^^^^^^^^^^^^^^ expected 1 lifetime parameter
I marked it with a lifetime parameter - why does it want another one? tried the object type with <'a> and appended it <'a> - neither of those attempts solved the problem.
CanvasRenderer is parameterized over a lifetime, so you need to state what that lifetime is:
pub struct ShapeRenderer<'a> {
core_renderer: &'a mut CanvasRenderer<'a>,
// ^^^^
}
However, this structure doesn't seem to have much purpose, it only adds indirection. Why have a reference to a thing that only has a reference? Skip the middleman:
pub struct ShapeRenderer<'a> {
core_renderer: CanvasRenderer<'a>,
}