I am trying to decode APER messages and have issue creating nested structs with traits. I am using the asn1 library, but I have provided an example to illustrate the issue.
I need a model that can take a value that can be any struct (more or less).
Using boxes does not work since the serial trait is Sized. I cannot seem to get generics to work either since the decode function does not return the type.
How can I get past this?
use std::fmt::Error;
pub trait serial: Sized {
fn decode(&self) -> Result<Self, Error>;
}
pub struct Middle {
pub bottomCode: i32,
pub value: Box<dyn Bottom>,
}
impl serial for Middle {
fn decode(&self) -> Result<Self, Error> {
let code = 1;
if code == 1 {
Ok(Middle {
bottomCode: code,
value: Box::new(BottomA { a: 1 }),
})
} else {
Ok(Middle {
bottomCode: code,
value: Box::new(BottomB { b: 1 }),
})
}
}
}
pub trait Bottom: serial {}
pub struct BottomA {
pub a: i32,
}
impl Bottom for BottomA {}
pub struct BottomB {
pub b: i32,
}
impl Bottom for BottomB {}
error[E0038]: the trait `Bottom` cannot be made into an object
--> src/lib.rs:9:5
|
9 | pub value: Box<dyn Bottom>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bottom` cannot be made into an object
|
= note: the trait cannot require that `Self : Sized`
error[E0277]: the trait bound `BottomA: serial` is not satisfied
--> src/lib.rs:32:6
|
32 | impl Bottom for BottomA {}
| ^^^^^^ the trait `serial` is not implemented for `BottomA`
error[E0277]: the trait bound `BottomB: serial` is not satisfied
--> src/lib.rs:37:6
|
37 | impl Bottom for BottomB {}
| ^^^^^^ the trait `serial` is not implemented for `BottomB`
The serial trait represents the APerElement trait I'm trying to use from the ASN1 library, thus the return type and signature.
I want to be able to call Middle.decode() which will result in all children being decoded alongside it, and any children of those etc. For that reason, it made sense to implement serial for it as well.
How would someone achieve such behavior? Is it possible?
Related
pub trait Draw {
type item;
fn draw(&self);
}
pub struct Screen {
pub comps: Vec<Box<dyn Draw>>,
}
impl Screen {
pub fn run(&self) {
for comp in self.comps.iter() {
comp.draw();
}
}
}
I got this error message:
error[E0191]: the value of the associated type `item` (from trait `Draw`) must be specified
--> src/main.rs:12:28
|
6 | type item;
| ---------- `item` defined here
...
12 | pub comps: Vec<Box<dyn Draw>>,
| ^^^^ help: specify the associated type: `Draw<item = Type>`
How can I fix it?
You need to specify the item concrete type while specifying trait object vec.
i.e.
pub struct Screen {
pub comps: Vec<Box<dyn Draw<item = i64>>>,
}
I'm trying to export the following struct:
#[wasm_bindgen]
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum TokenType {
KeywordLiteral,
NumberLiteral,
Operator,
Separator,
StringLiteral,
}
#[wasm_bindgen]
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct Token {
pub typ: TokenType,
pub val: String,
}
but I'm getting:
error[E0277]: the trait bound `token::TokenType: std::marker::Copy` is not satisfied
--> src\tokenizer\token.rs:17:14
|
14 | #[wasm_bindgen]
| --------------- required by this bound in `__wbg_get_token_typ::assert_copy`
...
17 | pub typ: TokenType,
| ^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `token::TokenType`
as well as:
error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied
--> src\tokenizer\token.rs:18:14
|
14 | #[wasm_bindgen]
| --------------- required by this bound in `__wbg_get_token_val::assert_copy`
...
18 | pub val: String,
I can add #[derive(Copy)] to TokenType but not to String.
I'm new to rust so help is really appreciated.
According to wasm-bindgen#1985, public fields of structs are required to be Copy in order for the automatically generated accessors to function.
You can either make the fields private, or annotate them with #[wasm_bindgen(skip)], and then implement a getter and setter directly.
There is an example provided in wasm-bindgen's docs describing how to write these:
#[wasm_bindgen]
pub struct Baz {
field: i32,
}
#[wasm_bindgen]
impl Baz {
#[wasm_bindgen(constructor)]
pub fn new(field: i32) -> Baz {
Baz { field }
}
#[wasm_bindgen(getter)]
pub fn field(&self) -> i32 {
self.field
}
#[wasm_bindgen(setter)]
pub fn set_field(&mut self, field: i32) {
self.field = field;
}
}
When objects are shared between wasm and js, the js object only contains the pointer to the struct inside the wasm runtime's memory. When you access fields from JS, it goes through a defined property which makes a function call to the wasm code asking it for the property value (you can think of the wasm memory as a bit UInt8Array).
Requiring the objects to be Copy is probably done to avoid surprises when auto-generating these getters and setters. If you implement them manually, you can have the same behaviour in JS and be able to control what's being set on the rust side.
I'm trying to create an iterator that will add one element to the start of a vector
struct World {
player: Object,
npcs: Vec<Object>,
}
impl World {
pub fn all_objects(&mut self) -> ???
}
I understand I need to use the chain function for this but I'm not sure how, or what will the return type be
Maybe someone can explain to me how to use it?
The exact types of iterators can be complex. A fast way to determine them is to try to return the wrong type on purpose, and see what the compiler says:
struct Object;
struct World {
player: Object,
npcs: Vec<Object>,
}
impl World {
pub fn all_objects(&mut self) -> () {
std::iter::once(&self.player).chain(self.npcs.iter())
}
}
(Permalink to the playground)
This gives:
error[E0308]: mismatched types
--> src/lib.rs:10:9
|
9 | pub fn all_objects(&mut self) -> () {
| -- expected `()` because of return type
10 | std::iter::once(&self.player).chain(self.npcs.iter())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try adding a semicolon: `;`
| |
| expected `()`, found struct `std::iter::Chain`
|
= note: expected unit type `()`
found struct `std::iter::Chain<std::iter::Once<&Object>, std::slice::Iter<'_, Object>>`
and indeed if you replace that, it will compile:
struct Object;
struct World {
player: Object,
npcs: Vec<Object>,
}
impl World {
pub fn all_objects(
&mut self,
) -> std::iter::Chain<std::iter::Once<&Object>, std::slice::Iter<'_, Object>> {
std::iter::once(&self.player).chain(self.npcs.iter())
}
}
But the exact type usually doesn't matter with iterators, so you can simply specify that your function returns some kind of iterator:
struct Object;
struct World {
player: Object,
npcs: Vec<Object>,
}
impl World {
pub fn all_objects(&mut self) -> impl Iterator<Item = &Object> {
std::iter::once(&self.player).chain(self.npcs.iter())
}
}
(Permalink to the playground)
This is also more flexible, as it will let you change the implementation without affecting the users of the function.
I've got three examples, one using Vec one using SmallVec, and one with my own implementation of SmallVec. The ones using Vec and my own SmallVec compile but the one using the real SmallVec does not.
Working example using Vec
use std::borrow::Cow;
use std::collections::HashMap;
pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}
/// IMPORTANT PART IS HERE: `Vec<Cow<'a, str>>`
pub struct ItemTraitReturns<'a>(Vec<Cow<'a, str>>);
/// this implementation only takes items with static lifetime (but other implementations also might have different lifetimes)
pub struct MyTraitStruct {
map: HashMap<usize, ItemTraitReturns<'static>>,
}
impl MyTrait for MyTraitStruct {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
// Works as expected: I expect that I can return `&ItemTraitReturns<'_>`
// when I have `&ItemTraitReturns<'static>` (since 'static outlives everything).
temp
// Will return `&ItemTraitReturns<'_>`
}
}
Failing example with SmallVec
Uses SmallVec instead of Vec with no other changes.
use smallvec::SmallVec;
use std::borrow::Cow;
use std::collections::HashMap;
pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}
/// IMPORTANT PART IS HERE: Uses SmallVec instead of Vec
pub struct ItemTraitReturns<'a>(SmallVec<[Cow<'a, str>; 2]>);
/// this implementation only takes items with static lifetime (but other implementations also might have different lifetimes)
pub struct MyTraitStruct {
map: HashMap<usize, ItemTraitReturns<'static>>,
}
impl MyTrait for MyTraitStruct {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
temp
}
}
error[E0308]: mismatched types
--> src/lib.rs:23:9
|
23 | temp
| ^^^^ lifetime mismatch
|
= note: expected type `&ItemTraitReturns<'_>`
found type `&ItemTraitReturns<'static>`
note: the anonymous lifetime #1 defined on the method body at 18:5...
--> src/lib.rs:18:5
|
18 | / fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
19 | | let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
20 | | // Error:
21 | | // = note: expected type `&demo2::ItemTraitReturns<'_>`
22 | | // found type `&demo2::ItemTraitReturns<'static>`
23 | | temp
24 | | }
| |_____^
= note: ...does not necessarily outlive the static lifetime
Working example with my own SmallVec
When I implement my own (very naive) SmallVec<[T; 2]> (called NaiveSmallVec2<T>) the code also compiles... Very strange!
use std::borrow::Cow;
use std::collections::HashMap;
/// This is a very naive implementation of a SmallVec<[T; 2]>
pub struct NaiveSmallVec2<T> {
item1: Option<T>,
item2: Option<T>,
more: Vec<T>,
}
impl<T> NaiveSmallVec2<T> {
pub fn push(&mut self, item: T) {
if self.item1.is_none() {
self.item1 = Some(item);
} else if self.item2.is_none() {
self.item2 = Some(item);
} else {
self.more.push(item);
}
}
pub fn element_by_index(&self, index: usize) -> Option<&T> {
match index {
0 => self.item1.as_ref(),
1 => self.item2.as_ref(),
_ => self.more.get(index - 2),
}
}
}
pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}
/// IMPORTANT PART IS HERE: Uses NaiveSmallVec2
pub struct ItemTraitReturns<'a>(NaiveSmallVec2<Cow<'a, str>>);
/// only takes items with static lifetime
pub struct MyTraitStruct {
map: HashMap<usize, ItemTraitReturns<'static>>,
}
impl MyTrait for MyTraitStruct {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
// astonishingly this works!
temp
}
}
I expect the SmallVec version to compile like the Vec version does. I don't understand why in some cases (in the case of Vec) &ItemTraitReturns<'static> can be converted to &ItemTraitReturns<'_> and in some cases (SmallVec) it's not possible (I don't see the influence of Vec / SmallVec).
I don't want to change lifetimes of this trait:
pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}
... since when using the trait I don't care about lifetimes (this should be an implementation detail) ... but still would like to use SmallVec.
This difference appears to be caused by a difference in variance between Vec and SmallVec. While Vec<T> is covariant in T, SmallVec appears to be invariant. As a result, SmallVec<[&'a T; 1]> can't be converted to SmallVec<[&'b T; 1]> even when lifetime 'a outlives 'b and the conversion should be safe. Here is a minimal example triggering the compiler error:
fn foo<'a, T>(x: SmallVec<[&'static T; 1]>) -> SmallVec<[&'a T; 1]> {
x // Compiler error here: lifetime mismatch
}
fn bar<'a, T>(x: Vec<&'static T>) -> Vec<&'a T> {
x // Compiles fine
}
This appears to be a shortcoming of SmallVec. Variance is automatically determined by the compiler, and it is sometimes cumbersome to convince the compiler that it is safe to assume that a type is covariant.
There is an open Github issue for this problem.
Unfortunately, I don't know a way of figuring out the variance of a type based on its public interface. Variance is not included in the documentation, and depends on the private implementation details of a type. You can read more on variance in the language reference or in the Nomicon.
I have two structs, A and B, and I want to use a HashMap<A, B>. I have a piece of code like this:
use std::collections::HashMap;
pub struct A {
x: i32,
y: i32,
title: String,
}
pub struct B {
a: u32,
b: u32,
}
fn main() {
let map = HashMap::new();
map.insert(
A {
x: 10,
y: 20,
title: "test".to_string(),
},
B { a: 1, b: 2 },
);
}
But the compiler gives me these errors:
error[E0277]: the trait bound `A: std::cmp::Eq` is not satisfied
--> src/main.rs:16:9
|
16 | map.insert(
| ^^^^^^ the trait `std::cmp::Eq` is not implemented for `A`
error[E0277]: the trait bound `A: std::hash::Hash` is not satisfied
--> src/main.rs:16:9
|
16 | map.insert(
| ^^^^^^ the trait `std::hash::Hash` is not implemented for `A`
I know that I must implement these traits, but after hours of searching the web, I have found nothing about implementing them.
My actual code is more complicated, and my structs contain other structs (I've edited the code).
I've implemented the Hash trait:
impl std::hash::Hash for A {
fn hash<H>(&self, state: &mut H)
where
H: std::hash::Hasher,
{
state.write_i32(self.x);
state.finish();
}
}
I made an implementation for PartialEq also:
impl PartialEq for A {
fn eq(&self, other: &A) -> bool {
self.x == other.x
}
}
But the compiler continues to complain, this time about Eq:
error[E0277]: the trait bound `A: std::cmp::Eq` is not satisfied
--> src/main.rs:16:9
|
16 | map.insert(
| ^^^^^^ the trait `std::cmp::Eq` is not implemented for `A`
How can I implement Eq? Why is there no implementation in the docs?
Eq is what we call a marker trait: it has no method on its own, it is just a way for the programmer to express that the struct verifies a certain property. You can implement it like this:
impl Eq for Application {}
Or alternatively, use #[derive(Eq)] on top of the Application declaration
Eq is a trait bound by PartialEq. This means that you can implement it only on structs that also implement PartialEq (which is the case here). By implementing Eq, you make the promise that your implementation of PartialEq is reflexive (see the docs for what it means).
You can have the compiler derive these instances for you by inserting the following before your struct declaration:
#[derive(PartialEq, Eq, Hash)]
pub struct A {
// ...
}
You could also implement them manually instead. If you want to do that, you should read the documentation on traits, Eq and Hash.
This is how the Rust documentation says you write your own implementation of Hash:
use std::hash::{Hash, Hasher};
struct Person {
id: u32,
name: String,
phone: u64,
}
impl Hash for Person {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
self.phone.hash(state);
}
}
Source: https://doc.rust-lang.org/std/hash/trait.Hash.html