Rust macros - structs, enums, conversion [closed] - rust

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last month.
Improve this question
have question related to (maybe) rust macros, or some other smart way to implement this.
Code below comes from 3rd-party and can't be modified
pub struct Message0;
pub struct Message1;
pub struct Message {
pub payload: Option<Payload>,
}
pub enum Payload {
PayloadMessage0(Message0),
PayloadMessage1(Message1),
}
pub fn third_party_send(msg: Message) {
// ...
}
It is desired to somehow implement this function ( or some other variant that will not require to implement separate functions for each of Message0 & Message1 types )
pub fn send<T>(specific_msg: T) {
third_party_send(Message {
payload: Some(Payload::PayloadMessage???(specific_msg)),
});
}
I am new to rust, still a bit confused by macros & generics ....

I don't think you can get around having to create a function for each variant, but you
can create a macro doing it for you.
I'd then wrap those in a trait to convert the Message? to a Payload and enable your generic function:
pub trait ToPayload {
fn to_payload(self) -> Payload;
}
macro_rules! to_payload {
($message:ident, $payload:ident) => {
impl ToPayload for $message {
fn to_payload(self) -> Payload {
Payload::$payload(self)
}
}
}
}
to_payload!{ Message0, PayloadMessage0 }
to_payload!{ Message1, PayloadMessage1 }
pub fn send<T: ToPayload>(specific_msg: T) {
third_party_send(Message {
payload: Some(specific_msg.to_payload())
});
}
fn main() {
send(Message0);
send(Message1);
}
Playground
With the unstable feature concat_idents you could remove some repetition from the macro invocation
#![feature(concat_idents)]
macro_rules! to_payload {
($message:ident) => {
impl ToPayload for $message {
fn to_payload(self) -> Payload {
use Payload::*;
concat_idents!(Payload, $message) (self)
}
}
};
}
to_payload! { Message0 }

Related

Is it considered bad style to bring the variants of an enum into scope for its impl? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 months ago.
Improve this question
Consider the following code:
enum Foo {
Var1(String),
Var2(i32),
}
impl Foo {
fn bar(&self) -> i32 {
match self {
Foo::Var1(_) => 0,
Foo::Var2(x) => *x,
}
}
}
Now one might want to make the code look a bit cleaner by bringing the variants of the enum into scope of the impl block like this:
enum Foo {
Var1(String),
Var2(i32),
}
use Foo::*;
impl Foo {
fn bar(&self) -> i32 {
match self {
Var1(_) => 0,
Var2(x) => *x,
}
}
}
Is this common among Rustaceans or is this considered bad style?
Instead of importing it globally (which might clutter the global scope with the enum members) you can put the use statement in the local scope. I'd consider this cleaner, but of course it's personal preference.
enum Foo {
Var1(String),
Var2(i32),
}
impl Foo {
fn bar(&self) -> i32 {
use Foo::*;
match self {
Var1(_) => 0,
Var2(x) => *x,
}
}
}

How to test value of struct field that has dynamic type [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 months ago.
Improve this question
I'm trying to write test for my simple log structure, which is supposed to hold many types such as console, buffer, files etc.
pub struct Logger
{
pub files: Vec<Box<dyn std::io::Write>>
}
I want to test it similar as it is in chapter of rust doc.
let writer = Box::new(Vec::new());
let mut log: log::Logger = log::Logger
{
files: vec![Box::new(writer)],
};
log.info("Info message");
// Check if buffer value is correct.
assert_eq!(log.files[0], b"[Info]: Info message"); // wrong types
I tried by getting back object using things like as_mut(), clone(), but I don't know how to cast it to Vec. Also at this point it is not possibly to use writer object due to move.
Can i somehow compare those 2 values for this test or field should be write differently( if yes, how?)
You can try using std::any
use std::any::Any;
trait MyTrait: std::io::Write {
fn as_any(&self) -> &dyn Any;
}
impl<T> MyTrait for T
where
T: std::io::Write + Any,
{
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct Logger {
pub files: Vec<Box<dyn MyTrait>>,
}
fn main() {
let writer = Box::new(Vec::new());
let mut log: Logger = Logger {
files: vec![Box::new(writer)],
};
// Check if buffer value is correct.
assert_eq!(
log.files[0].as_any().downcast_ref::<Vec<u8>>().unwrap(),
b"[Info]: Info message"
); // wrong types
}
Run on Rust playground
Maybe just do a simple downcast in unit test if you don't want to add any extra code.
use std::any::Any;
pub struct Logger {
pub files: Vec<Box<dyn std::io::Write>>,
}
fn main() {
let buffer = Box::new(Vec::new());
let mut log: Logger = Logger {
files: vec![buffer],
};
let writer = log.files[0].as_mut();
writer.write(b"[Info]: Info message").unwrap();
let buffer = unsafe { &*(&log.files[0] as *const dyn Any as *const Box<Vec<u8>>) };
assert_eq!(buffer.as_slice(), b"[Info]: Info message");
}

Can I declare an enum value that takes a String or &str without needing additional functions?

I have an enum with a String:
enum MyLovelyEnum {
Thing(String),
}
For tests, I would like to be able to pass in a &'static str to avoid MyLovelyEnum::Thing("abc".to_string) over and over.
I found that you can do this nicely with structs with a constructor:
// From: https://hermanradtke.com/2015/05/06/creating-a-rust-function-that-accepts-string-or-str.html
struct Person {
name: String,
}
impl Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
}
fn main() {
let person = Person::new("Herman");
let person = Person::new("Herman".to_string());
}
I know I can use lifetimes or Cow as described in What's the best practice for str/String values in Rust enums? or I can make my own function.
Is there something close to the example in the blog post for enums? e.g.
// this is the kind of thing I am after but this specifically is not correct syntax
enum MyLovelyEnum {
Thing<S: Into<String>>(S)
}
You can create a generic enum:
enum MyLovelyEnum<S>
where
S: Into<String>,
{
Thing(S),
}
MyLovelyEnum::Thing("a");
MyLovelyEnum::Thing("b".to_string());
I likely wouldn't do that in my code, instead opting to create a constructor, much like the blog post you linked:
enum MyLovelyEnum {
Thing(String),
}
impl MyLovelyEnum {
fn thing(s: impl Into<String>) -> Self {
MyLovelyEnum::Thing(s.into())
}
}
MyLovelyEnum::thing("a");
MyLovelyEnum::thing("b".to_string());

Is there a way to implement the same thing for lots of structs automatically?

As you can see, all the types below have the same structure:
type Response = rtsp_types::Response<Body>;
struct PlayResponse(Response);
struct DescribeResponse(Response);
struct SetupResponse(Response);
struct PauseResponse(Response);
struct TeardownResponse(Response);
struct RecordResponse(Response);
struct UnauthorizedResponse(Response);
And I want to do the same implementation for them:
impl From<Response> for PlayResponse {
fn from(response: Response) -> Self {
PlayResponse(response)
}
}
impl From<Response> for DescribeResponse {
fn from(response: Response) -> Self {
DescribeResponse(response)
}
}
//...
but I don't want to be repetitive.
I know that I could do an enum with all of them, but this solution does not apply to my problem for reasons that are too large to put here.
What would be the be best way to implement From for all these structs, just like the example implementation I did? Macros? Isn't there a way to do a generic implementation over T?
The traditional way is just to make a declarative macro that implements From for your various types:
type Response = rtsp_types::Response<Body>;
struct PlayResponse(Response);
struct DescribeResponse(Response);
struct SetupResponse(Response);
struct PauseResponse(Response);
struct TeardownResponse(Response);
struct RecordResponse(Response);
struct UnauthorizedResponse(Response);
macro_rules! impl_from_response {
($t:ident) => {
impl From<Response> for $t {
fn from(response: Response) -> Self {
$t(response)
}
}
}
}
impl_from_response!(PlayResponse);
impl_from_response!(DescribeResponse);
impl_from_response!(SetupResponse);
impl_from_response!(PauseResponse);
impl_from_response!(TeardownResponse);
impl_from_response!(RecordResponse);
impl_from_response!(UnauthorizedResponse);
You can use the duplicate crate to easily do this:
use duplicate::duplicate;
type Response = rtsp_types::Response<Body>;
duplicate!{
[
Name;
[PlayResponse];
[DescribeResponse];
[SetupResponse];
[PauseResponse];
[TeardownResponse];
[RecordResponse];
[UnauthorizedResponse];
]
struct Name(Response);
impl From<Response> for Name {
fn from(response: Response) -> Self {
Name(response)
}
}
}
This will both declare each struct and its implementation, substituting Name for the name of each struct.

How can I return a reference to the inner data of a RefCell<Option<T>>>? [duplicate]

This question already has answers here:
How do I return a reference to something inside a RefCell without breaking encapsulation?
(3 answers)
Closed 2 years ago.
I'm creating a lazily loading struct:
pub struct LazyData<T, U:FnOnce() -> T> {
data: RefCell<Option<T>>,
generator: RefCell<Option<U>>,
}
impl<T,U:FnOnce() -> T> LazyData<T,U> {
pub fn new(generator:U) -> LazyData<T,U> {
LazyData {
data: RefCell::new(None),
generator: RefCell::new(Some(generator)),
}
}
...
}
I've tried to implement a get method that will return the lazily loaded data:
pub fn init(&self) {
if self.data.borrow().is_none() {
let generator = self.generator.replace(None).unwrap();
let _none = self.data.replace(Some(generator()));
};
}
pub fn get(&self) -> &T {
self.init();
self.data.borrow().as_ref().unwrap()
}
But the compiler complains that I'm returning a value referencing data owned by the current function. This error makes sense to me, but I'm not sure how to work around it.
Here's a link to a full example.
Found a workaround here! I can use Ref::map to get to the inner data:
pub fn get(&self) -> Ref<T> {
self.init();
Ref::map(self.data.borrow(), |borrow| {
borrow.as_ref().unwrap()
})
}
This method now returns Ref instead of &T, but I think that should be fine.

Resources