Mixing high-order functions and Result - rust

I'm not really sure how to write a high-order function that wants to return an error of its own and make it look nice when I use it on another functions that also return a Result.
This is hard to describe. Here's an example:
use std::fs;
use std::io;
use std::io::Read;
use std::fmt;
use std::error::Error;
#[derive(Debug)]
struct MyError;
impl Error for MyError {}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MyError")
}
}
fn log_something() -> Result<(), MyError> {
unimplemented!()
}
fn log_and_call<T>(f: &dyn Fn() -> T) -> Result<T, MyError> {
log_something()?;
Ok(f())
}
fn the_answer() -> i32 {
42
}
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = fs::File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn main() {
let _logged_answer: Result<i32, MyError> = log_and_call(&the_answer);
let _logged_username: Result<Result<String, io::Error>, MyError> = log_and_call(&read_username_from_file);
}
How do I avoid having that Result<Result<String, io::Error>, MyError> there? I'd like to have Result<String, Error>, or maybe something even more sensible.
Maybe there's a more idomatic way to do this?

You can do this with the help of traits:
fn log_and_call<T: ToResult>(f: &dyn Fn() -> T) -> Result<T::Value, MyError> {
log_something()?;
f().to_result()
}
fn the_answer() -> i32 {
42
}
trait ToResult {
type Value;
fn to_result(self) -> Result<Self::Value, MyError>;
}
impl<T, E: Error> ToResult for Result<T, E> {
type Value = T;
fn to_result(self) -> Result<Self::Value, MyError> {
self.map_err(|err| MyError::from(err))
}
}
impl ToResult for i32 {
type Value = i32;
fn to_result(self) -> Result<Self::Value, MyError> {
Ok(self)
}
}
fn main() {
let _logged_answer: Result<i32, MyError> = log_and_call(&the_answer);
let _logged_username: Result<String, MyError> = log_and_call(&read_username_from_file);
}
Playground
I don't exactly know your use case, so if you just want to grab the value inside the result wherever you are calling, Box<dyn std::error::Error> can be used at return position:
fn main() -> Result<(), Box<dyn Error>> {
let _logged_answer: i32 = log_and_call(&the_answer)?;
let _logged_username: String = log_and_call(&read_username_from_file)??;
Ok(())
}
Playground
Another way could be to change log_and_call to this:
fn log_and_call<T, E: Error>(f: &dyn Fn() -> Result<T, E>) -> Result<T, MyError> where MyError: From<E> {
log_something()?;
Ok(f()?)
}
and then calling the function like:
fn main() {
let _logged_answer: Result<i32, MyError> = log_and_call(&||-> Result<_, MyError> {Ok(the_answer())});
let _logged_username: Result<String, MyError> = log_and_call(&read_username_from_file);
}
Playground
You could define a macro or a function to do this wrapping in a closure returning Result:
macro_rules! wrap {
($($content:tt)+) => {
||-> Result<_, MyError> {Ok($($content)+)}
};
}
fn main() {
let _logged_answer: Result<i32, MyError> = log_and_call(&wrap!(the_answer()));
let _logged_username: Result<String, MyError> = log_and_call(&read_username_from_file);
}
Playground
Another solution could be to have something like flatten_result as suggested by Aplet in question comments.

Related

Trait method with generic argument

The following code provides a container in which to store prepared functions for asynchronous code so that you can pass it somewhere for execution:
use std::collections::HashMap;
use futures::future::{Future, BoxFuture};
trait AsyncFn {
fn call(&self, arg: String) -> BoxFuture<'static, ()>;
}
impl<T, F> AsyncFn for T
where
T: Fn(String) -> F,
F: Future<Output = ()> + 'static + Send,
{
fn call(&self, arg: String) -> BoxFuture<'static, ()> {
Box::pin(self(arg))
}
}
async fn async_test(data: String) -> () {
println!("{data}");
}
#[async_std::main]
async fn main() {
let mut callables: HashMap<String, Box<dyn AsyncFn>> = HashMap::new();
callables.insert("test_func".to_string(), Box::new(async_test));
let awaitable = callables.get("test_func").unwrap();
awaitable.call("test string argument1".to_string()).await;
let awaitable = callables.get("test_func").unwrap();
awaitable.call("test string argument2".to_string()).await;
}
But this greatly limits the set of functionality called in this way. I can only pass strings.
Is it possible to describe the trait more universally, so that AsyncFn can accept a heap reference to any type?
I tried to do it like this:
trait AsyncFn<B> {
fn call(&self, arg: Box<B>) -> BoxFuture<'static, ()>;
}
impl<T, B, F> AsyncFn<B> for T
where
T: Fn(Box<B>) -> F,
F: Future<Output = ()> + 'static + Send,
{
fn call(&self, arg: Box<B>) -> BoxFuture<'static, ()> {
Box::pin(self(arg))
}
}
async fn async_test(data: Box<String>) -> () {
println!("String data = {data}");
}
async fn async_test2(data: Box<u64>) -> () {
println!("u64 data = {data}");
}
But this still restricts the type that I send to the container:
let mut callables: HashMap<String, Box<dyn AsyncFn<String>>> = HashMap::new();
callables.insert("test_func".to_string(), Box::new(async_test));
// ! callables.insert("test_func2".to_string(), Box::new(async_test2));
I should probably try wrapping this in an enum or some other type, but maybe there is a more elegant solution?

how to solve cyclic-dependency & Iterator lifetime problem?

Rustaceans. when I start to write a BloomFilter example in rust. I found I have serveral problems have to solve. I struggle to solve them but no progress in a day. I need help, any suggestion will help me a lot, Thanks.
Problems
How to solve lifetime when pass a Iterator into another function?
// let bits = self.hash(value); // how to solve such lifetime error without use 'static storage?
// Below is a workaround code but need to computed in advanced.
let bits = Box::new(self.hash(value).collect::<Vec<u64>>().into_iter());
self.0.set(bits);
How to solve cyclic-dependency between struts without modify lower layer code, e.g: bloom_filter ?
// cyclic-dependency:
// RedisCache -> BloomFilter -> Storage
// | ^
// ------------<impl>------------
//
// v--- cache ownership has moved here
let filter = BloomFilter::by(Box::new(cache));
cache.1.replace(filter);
Since rust does not have null value, How can I solve the cyclic-dependency initialization without any stubs?
let mut cache = RedisCache(
Client::open("redis://localhost").unwrap(),
// I found can use Weak::new() to solve it,but need to downgrade a Rc reference.
// v-- need a BloomFilter stub to create RedisCache
RefCell::new(BloomFilter::new()),
);
Code
#![allow(unused)]
mod bloom_filter {
use std::{hash::Hash, marker::PhantomData};
pub type BitsIter = Box<dyn Iterator<Item = u64>>;
pub trait Storage {
fn set(&mut self, bits: BitsIter);
fn contains_all(&self, bits: BitsIter) -> bool;
}
pub struct BloomFilter<T: Hash>(Box<dyn Storage>, PhantomData<T>);
impl<T: Hash> BloomFilter<T> {
pub fn new() -> BloomFilter<T> {
return Self::by(Box::new(ArrayStorage([0; 5000])));
struct ArrayStorage<const N: usize>([u8; N]);
impl<const N: usize> Storage for ArrayStorage<N> {
fn set(&mut self, bits: BitsIter) {
let size = self.0.len() as u64;
bits.map(|bit| (bit % size) as usize)
.for_each(|index| self.0[index] = 1);
}
fn contains_all(&self, bits: BitsIter) -> bool {
let size = self.0.len() as u64;
bits.map(|bit| (bit % size) as usize)
.all(|index| self.0[index] == 1)
}
}
}
pub fn by(storage: Box<dyn Storage>) -> BloomFilter<T> {
BloomFilter(storage, PhantomData)
}
pub fn add(&mut self, value: T) {
// let bits = self.hash(value); // how to solve such lifetime error?
let bits = Box::new(self.hash(value).collect::<Vec<u64>>().into_iter());
self.0.set(bits);
}
pub fn contains(&self, value: T) -> bool {
// lifetime problem same as Self::add(T)
let bits = Box::new(self.hash(value).collect::<Vec<u64>>().into_iter());
self.0.contains_all(bits)
}
fn hash<'a, H: Hash + 'a>(&self, _value: H) -> Box<dyn Iterator<Item = u64> + 'a> {
todo!()
}
}
}
mod spi {
use super::bloom_filter::*;
use redis::{Client, Commands, RedisResult};
use std::{
cell::RefCell,
rc::{Rc, Weak},
};
pub struct RedisCache<'a>(Client, RefCell<BloomFilter<&'a str>>);
impl<'a> RedisCache<'a> {
pub fn new() -> RedisCache<'a> {
let mut cache = RedisCache(
Client::open("redis://localhost").unwrap(),
// v-- need a BloomFilter stub to create RedisCache
RefCell::new(BloomFilter::new()),
);
// v--- cache ownership has moved here
let filter = BloomFilter::by(Box::new(cache));
cache.1.replace(filter);
return cache;
}
pub fn get(&mut self, key: &str, load_value: fn() -> Option<String>) -> Option<String> {
let filter = self.1.borrow();
if filter.contains(key) {
if let Ok(value) = self.0.get::<&str, String>(key) {
return Some(value);
}
if let Some(actual_value) = load_value() {
let _: () = self.0.set(key, &actual_value).unwrap();
return Some(actual_value);
}
}
return None;
}
}
impl<'a> Storage for RedisCache<'a> {
fn set(&mut self, bits: BitsIter) {
todo!()
}
fn contains_all(&self, bits: BitsIter) -> bool {
todo!()
}
}
}
Updated
First, thanks #Colonel Thirty Two give me a lot of information that I haven't mastered and help me fixed the problem of the iterator lifetime.
The cyclic-dependency I have solved by break the responsibility of the Storage into another struct RedisStorage without modify the bloom_filter module, but make the example bloated. Below is their relationships:
RedisCache -> BloomFilter -> Storage <---------------
| |
|-------> redis::Client <- RedisStorage ---<impl>---
I realized the ownership & lifetime system is not only used by borrow checker, but also Rustaceans need a bigger front design to obey the rules than in a GC language, e.g: java. Am I right?
Final Code
mod bloom_filter {
use std::{
hash::{Hash, Hasher},
marker::PhantomData,
};
pub type BitsIter<'a> = Box<dyn Iterator<Item = u64> + 'a>;
pub trait Storage {
fn set(&mut self, bits: BitsIter);
fn contains_all(&self, bits: BitsIter) -> bool;
}
pub struct BloomFilter<T: Hash>(Box<dyn Storage>, PhantomData<T>);
impl<T: Hash> BloomFilter<T> {
#[allow(unused)]
pub fn new() -> BloomFilter<T> {
return Self::by(Box::new(ArrayStorage([0; 5000])));
struct ArrayStorage<const N: usize>([u8; N]);
impl<const N: usize> Storage for ArrayStorage<N> {
fn set(&mut self, bits: BitsIter) {
let size = self.0.len() as u64;
bits.map(|bit| (bit % size) as usize)
.for_each(|index| self.0[index] = 1);
}
fn contains_all(&self, bits: BitsIter) -> bool {
let size = self.0.len() as u64;
bits.map(|bit| (bit % size) as usize)
.all(|index| self.0[index] == 1)
}
}
}
pub fn by(storage: Box<dyn Storage>) -> BloomFilter<T> {
BloomFilter(storage, PhantomData)
}
pub fn add(&mut self, value: T) {
self.0.set(self.hash(value));
}
pub fn contains(&self, value: T) -> bool {
self.0.contains_all(self.hash(value))
}
fn hash<'a, H: Hash + 'a>(&self, value: H) -> BitsIter<'a> {
Box::new(
[3, 11, 31, 71, 131]
.into_iter()
.map(|salt| SimpleHasher(0, salt))
.map(move |mut hasher| hasher.hash(&value)),
)
}
}
struct SimpleHasher(u64, u64);
impl SimpleHasher {
fn hash<H: Hash>(&mut self, value: &H) -> u64 {
value.hash(self);
self.finish()
}
}
impl Hasher for SimpleHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
self.0 += bytes.iter().fold(0u64, |acc, k| acc * self.1 + *k as u64)
}
}
}
mod spi {
use super::bloom_filter::*;
use redis::{Client, Commands};
use std::{cell::RefCell, rc::Rc};
pub struct RedisCache<'a>(Rc<RefCell<Client>>, BloomFilter<&'a str>);
impl<'a> RedisCache<'a> {
pub fn new(client: Rc<RefCell<Client>>, filter: BloomFilter<&'a str>) -> RedisCache<'a> {
RedisCache(client, filter)
}
pub fn get<'f>(
&mut self,
key: &str,
load_value: fn() -> Option<&'f str>,
) -> Option<String> {
if self.1.contains(key) {
let mut redis = self.0.as_ref().borrow_mut();
if let Ok(value) = redis.get::<&str, String>(key) {
return Some(value);
}
if let Some(actual_value) = load_value() {
let _: () = redis.set(key, &actual_value).unwrap();
return Some(actual_value.into());
}
}
return None;
}
}
struct RedisStorage(Rc<RefCell<Client>>);
const BLOOM_FILTER_KEY: &str = "bloom_filter";
impl Storage for RedisStorage {
fn set(&mut self, bits: BitsIter) {
bits.for_each(|slot| {
let _: bool = self
.0
.as_ref()
.borrow_mut()
.setbit(BLOOM_FILTER_KEY, slot as usize, true)
.unwrap();
})
}
fn contains_all(&self, mut bits: BitsIter) -> bool {
bits.all(|slot| {
self.0
.as_ref()
.borrow_mut()
.getbit(BLOOM_FILTER_KEY, slot as usize)
.unwrap()
})
}
}
#[test]
fn prevent_cache_penetration_by_bloom_filter() {
let client = Rc::new(RefCell::new(Client::open("redis://localhost").unwrap()));
redis::cmd("FLUSHDB").execute(&mut *client.as_ref().borrow_mut());
let mut filter: BloomFilter<&str> = BloomFilter::by(Box::new(RedisStorage(client.clone())));
assert!(!filter.contains("Rust"));
filter.add("Rust");
assert!(filter.contains("Rust"));
let mut cache = RedisCache::new(client, filter);
assert_eq!(
cache.get("Rust", || Some("System Language")),
Some("System Language".to_string())
);
assert_eq!(
cache.get("Rust", || panic!("must never be called after cached")),
Some("System Language".to_string())
);
assert_eq!(
cache.get("Go", || panic!("reject to loading `Go` from external storage")),
None
);
}
}
pub type BitsIter = Box<dyn Iterator<Item = u64>>;
In this case, the object in the box must be valid for the 'static lifetime. This isn't the case for the iterator returned by hash - its limited to the lifetime of self.
Try replacing with:
pub type BitsIter<'a> = Box<dyn Iterator<Item = u64> + 'a>;
Or using generics instead of boxed trait objects.
So your RedisClient needs a BloomFilter, but the BloomFilter also needs the RedisClient?
Your BloomFilter should not use the RedisCache that itself uses the BloomFilter - that's a recipe for infinitely recursing calls (how do you know what calls to RedisCache::add should update the bloom filter and which calls are from the bloom filter?).
If you really have to, you need some form of shared ownership, like Rc or Arc. Your BloomFilter will also need to use a weak reference, or else the two objects will refer to each other and will never free.

Understanding Self within a trait impl

Im a bit confused by self. Could you please help me along?? Im trying to append or push a new value into the vec. This only shows me that I have no idea what self actually is. The error I'm getting is :
---- ^^^^ this call modifies `self` in-place
| |
| you probably want to use this value after calling the method...
= note: ...instead of the `()` output of method `push`
Why does the following work but...
trait AppendBar {
fn append_bar(self) -> Self;
}
impl AppendBar for String {
fn append_bar(self) -> Self{
self.to_string() + "Bar"
}
}
this and...
impl AppendBar for Vec<String> {
fn append_bar(self) -> Self{
let mut bar = vec![String::from("Bar")];
bar.push(self);
bar
}
}
this and...
impl AppendBar for Vec<String> {
fn append_bar(self) -> Self{
let bar_vec = vec!["Bar".to_string()];
self.append(bar_vec)
}
}
this do not?
trait AppendBar<T> {
fn append_bar(self) -> Self;
}
impl<T> AppendBar<T> for Vec<T> {
fn append_bar(self) -> Self{
self.push("Bar".to_string())
}
}
impl AppendBar for Vec<String> {
fn append_bar(self) -> Self{
let mut bar = vec![String::from("Bar")];
bar.push(self);
bar
}
}
Because self is a Vec<String> and you can't push a Vec<String> into a Vec<String> (which the error message tells you: "Expected String got Vec<String>").
impl AppendBar for Vec<String> {
fn append_bar(self) -> Self{
let bar_vec = vec!["Bar".to_string()];
self.append(bar_vec)
}
}
Because Vec::append doesn't return anything, so you need to return self yourself (or take &mut self as parameter and return nothing).
impl<T> AppendBar<T> for Vec<T> {
fn append_bar(self) -> Self{
self.push("Bar".to_string())
}
}
Because "Bar".to_string() is not guaranteed to be a T for all types T.

How can I lazy initialize/fill an `Option` with a fallible initializer?

Suppose I have a struct containing an Option<Resource>, where Resource is some type I need to work with that needs to allocate external resources (in the actual case, GPU memory) and so might fail. From within a method, I want to attempt the allocation if it hasn't been done already, but propagate or handle the error if it does fail.
If it weren't for the failure case, Option::get_or_insert_with would be perfect. As it is, the tidiest solution I have thought of involves an unwrap(), which is inelegant since it looks like a potential panic:
struct Container {
resource: Option<Resource>,
...
}
impl Container {
...
fn activate(&mut self) -> Result<(), Error> {
if self.resource.is_none() {
self.resource = Some(Resource::new()?);
}
let resource: &mut Resource = self.resource.as_mut().unwrap();
// ... now do things with `resource` ...
Ok(())
}
...
}
Is there a way to initialize the Option with less fuss than this? To be clear, I'm not solely looking for avoiding unwrap(), but also overall readability. If an alternative is much more complex and indirect, I'd rather stick with this.
Complete example code (on Rust Playground):
#[derive(Debug)]
struct Resource {}
#[derive(Debug)]
struct Error;
impl Resource {
fn new() -> Result<Self, Error> {
Ok(Resource {})
}
fn write(&mut self) {}
}
#[derive(Debug)]
struct Container {
resource: Option<Resource>,
}
impl Container {
fn new() -> Self {
Self { resource: None }
}
fn activate(&mut self) -> Result<(), Error> {
if self.resource.is_none() {
self.resource = Some(Resource::new()?);
}
self.resource.as_mut().unwrap().write();
Ok(())
}
}
fn main() {
Container::new().activate();
}
Indeed, get_or_insert_with() but returning Result<&mut T, E> is what you could use to simplify your code. However, as you already discovered Option doesn't have a e.g. try_get_or_insert_with() method.
Other workarounds would be similarly verbose. However, you could use a match and get_or_insert(), to avoid the unwrap() like this:
fn activate(&mut self) -> Result<(), Error> {
let res = match &mut self.resource {
Some(res) => res,
None => self.resource.get_or_insert(Resource::new()?),
};
res.write();
Ok(())
}
If this is used frequently, then you could also define your own try_get_or_insert_with() trait method, and implement it for Option<T>.
trait TryGetOrInsert<T> {
fn try_get_or_insert_with<E, F>(&mut self, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>;
}
impl<T> TryGetOrInsert<T> for Option<T> {
fn try_get_or_insert_with<E, F>(&mut self, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>,
{
match self {
Some(value) => Ok(value),
None => Ok(self.get_or_insert(f()?)),
}
}
}
Then now you can simplify your activate() method, to the following:
fn activate(&mut self) -> Result<(), Error> {
let res = self.resource.try_get_or_insert_with(|| Resource::new())?;
res.write();
Ok(())
}

Is there a way to get the field names of a struct in a macro?

Consider the following example:
struct S {
a: String,
b: String,
}
I have a macro which is called like this:
my_macro!(S);
I want to access the field names of the struct in the macro like this:
macro_rules! my_macro {
($t:ty) => {{
let field_names = get_field_names($t);
// do something with field_names
}};
}
I'm new to Rust and macros, so maybe I'm missing something obvious.
A macro is expanded during parsing, more or less; it has no access to the AST or anything like that—all it has access to is the stuff that you pass to it, which for my_macro!(S) is purely that there should be a type named S.
If you define the struct as part of the macro then you can know about the fields:
macro_rules! my_macro {
(struct $name:ident {
$($field_name:ident: $field_type:ty,)*
}) => {
struct $name {
$($field_name: $field_type,)*
}
impl $name {
// This is purely an example—not a good one.
fn get_field_names() -> Vec<&'static str> {
vec![$(stringify!($field_name)),*]
}
}
}
}
my_macro! {
struct S {
a: String,
b: String,
}
}
// S::get_field_names() == vec!["a", "b"]
… but this is, while potentially useful, often going to be a dubious thing to do.
Here is another possibility that does not require to write a macro (however, the field names will be resolved at run time):
extern crate rustc_serialize;
use rustc_serialize::json::{Encoder, Json};
use rustc_serialize::json::Json::Object;
use rustc_serialize::Encodable;
#[derive(Default, RustcEncodable)]
struct S {
a: String,
b: String,
}
fn main() {
let mut json = "".to_owned();
{
let mut encoder = Encoder::new(&mut json);
S::default().encode(&mut encoder).unwrap();
}
let json = Json::from_str(&json).unwrap();
if let Object(object) = json {
let field_names: Vec<_> = object.keys().collect();
println!("{:?}", field_names);
}
}
(this solution needs the rustc-serialize crate)
The derive(Default) has been added to avoid having to manually create a struct as you wanted (but a struct will still be created).
This solution works by encoding the struct to a String in JSON format and decoding it to a Json. From the Json object, we can extract the field names (if it is an Object variant).
A possibly more efficient method is to write its own encoder:
struct FieldNames {
names: Vec<String>,
}
impl FieldNames {
fn new() -> FieldNames {
FieldNames {
names: vec![],
}
}
}
struct FieldsEncoder<'a> {
fields: &'a mut FieldNames,
}
impl<'a> FieldsEncoder<'a> {
fn new(fields: &mut FieldNames) -> FieldsEncoder {
FieldsEncoder {
fields: fields,
}
}
}
type EncoderError = ();
impl<'a> Encoder for FieldsEncoder<'a> {
fn emit_struct<F>(&mut self, _name: &str, _len: usize, f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> {
f(self)
}
fn emit_struct_field<F>(&mut self, f_name: &str, _f_idx: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> {
self.fields.names.push(f_name.to_owned());
Ok(())
}
type Error = EncoderError;
fn emit_nil(&mut self) -> Result<(), Self::Error> { Err(()) }
fn emit_usize(&mut self, _v: usize) -> Result<(), Self::Error> { Err(()) }
fn emit_u64(&mut self, _v: u64) -> Result<(), Self::Error> { Err(()) }
fn emit_u32(&mut self, _v: u32) -> Result<(), Self::Error> { Err(()) }
fn emit_u16(&mut self, _v: u16) -> Result<(), Self::Error> { Err(()) }
fn emit_u8(&mut self, _v: u8) -> Result<(), Self::Error> { Err(()) }
fn emit_isize(&mut self, _v: isize) -> Result<(), Self::Error> { Err(()) }
fn emit_i64(&mut self, _v: i64) -> Result<(), Self::Error> { Err(()) }
fn emit_i32(&mut self, _v: i32) -> Result<(), Self::Error> { Err(()) }
fn emit_i16(&mut self, _v: i16) -> Result<(), Self::Error> { Err(()) }
fn emit_i8(&mut self, _v: i8) -> Result<(), Self::Error> { Err(()) }
fn emit_bool(&mut self, _v: bool) -> Result<(), Self::Error> { Err(()) }
fn emit_f64(&mut self, _v: f64) -> Result<(), Self::Error> { Err(()) }
fn emit_f32(&mut self, _v: f32) -> Result<(), Self::Error> { Err(()) }
fn emit_char(&mut self, _v: char) -> Result<(), Self::Error> { Err(()) }
fn emit_str(&mut self, _v: &str) -> Result<(), Self::Error> { Err(()) }
fn emit_enum<F>(&mut self, _name: &str, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_enum_variant<F>(&mut self, _v_name: &str, _v_id: usize, _len: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_enum_variant_arg<F>(&mut self, _a_idx: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_enum_struct_variant<F>(&mut self, _v_name: &str, _v_id: usize, _len: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_enum_struct_variant_field<F>(&mut self, _f_name: &str, _f_idx: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_tuple<F>(&mut self, _len: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_tuple_arg<F>(&mut self, _idx: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_tuple_struct<F>(&mut self, _name: &str, _len: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_tuple_struct_arg<F>(&mut self, _f_idx: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_option<F>(&mut self, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_option_none(&mut self) -> Result<(), Self::Error> { Err(()) }
fn emit_option_some<F>(&mut self, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_seq<F>(&mut self, _len: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_seq_elt<F>(&mut self, _idx: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_map<F>(&mut self, _len: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_map_elt_key<F>(&mut self, _idx: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
fn emit_map_elt_val<F>(&mut self, _idx: usize, _f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error> { Err(()) }
}
which can be used as such:
fn main() {
let mut fields = FieldNames::new();
{
let mut encoder = FieldsEncoder::new(&mut fields);
S::default().encode(&mut encoder).unwrap();
}
println!("{:?}", fields.names);
}
I wanted to do the same: access the field names of a struct. But with the added complication that the struct was already using a #[derive()] style macro, which is incompatible with macro_rules! solution. As I expect my use case to be rather common, here's a quick write-down of my solution.
My ultimate goal was to write a CSV header line corresponding to a struct Record with the csv crate, even when no record is written (writing records is usually done via serialize(), but we sometimes filter all records and still want a valid empty CSV file as output). This exact problem has also been formulated in another SO question and that this is not possible with just the csv crate is a known and currently unresolved issue.
My solution to the extra complication with the #[derive()] macro on the struct is to use the #[derive(FieldNamesAsArray)] macro defined by the struct-field-names-as-array crate.
You need to define the dependency in Cargo.toml:
[dependencies]
struct-field-names-as-array = "0.1"
Then you can simply annotate the struct Record in your something.rs module with the respective derive macro and use the resulting constant Record::FIELD_NAMES_AS_ARRAY for header writing:
// csv-specific imports
use csv::WriterBuilder;
use serde::Serialize;
// import for getting the field names array
use struct_field_names_as_array::FieldNamesAsArray;
// Serialize from serde, to write `Record`s systematically
// FieldNamesAsArray to get the field names
#[derive(Serialize,FieldNamesAsArray)]
struct Record {
field_1: String,
field_2: u64,
}
// ensure that serializing records does not write a header with
// the `.has_headers(false)`
let mut csv_writer = csv::WriterBuilder::new()
.has_headers(false)
.from_path("foo.csv")?;
// Manually write out the header.
csv_writer.write_record(Record::FIELD_NAMES_AS_ARRAY)?;
// `serialize()` records later, if some condition is met.
// But we also have a correct header if this condition is never met.
if some_condition {
csv_writer.serialize(Recor {
field_1: "some_string",
field_2: 71028743,
})?;
}

Resources