I'm trying to specify a template parameter of an imported class, so that I don't need to specify it each time I want to use it. Something like this:
use self::binary_heap_plus::BinaryHeap<T,MinComparator> as BinaryMinHeap<T>;
Is this possible?
Is this possible?
Yes it is possible like following:
pub type CustomResult<T> = Result<T, MyError>;
#[derive(Debug)]
pub enum MyError {
MyError1,
}
fn result_returner(prm: i32) -> CustomResult<i32> {
if prm == 1 {
Ok(5)
} else {
Err(MyError::MyError1)
}
}
And also you can make such like type name changings on import as well:
use std::collections::HashMap as CustomNamedMap;
fn main() {
let mut my_map = CustomNamedMap::new();
my_map.insert(1, 2);
println!("Value: {:?}", my_map[&1]);
}
Playground
Related
Specific Question
I need to pass csv variable from load_csv function to operations function.
Code
use polars::{
prelude::{CsvReader, SerReader},
frame::DataFrame,
error::PolarsResult
};
struct Boilerplate {}
impl Boilerplate {
fn load_csv(&self) -> PolarsResult<DataFrame> {
let csv = CsvReader::from_path("microsoft stock data.csv")?
.has_header(true)
.finish();
// println!("{:?}", csv);
csv
}
fn operations() {
}
}
fn main() {
let boilercode = Boilerplate{};
println!("{:?}", boilercode.load_csv());
}
What I've tried (But, Didn't Work)
Declared csv variable inside main function, then tried to access it in impl.
You can do something like this, storing your csv in the instance of your Boilerplate.
struct Boilerplate {
csv: PolarsResult<DataFrame>
}
impl Boilerplate {
fn new() -> Boilerplate {
Boilerplate {
csv: CsvReader::from_path("microsoft stock data.csv")?
.has_header(true)
.finish();
}
fn operations(&self) {
// work with self.csv
}
}
fn main() {
let boilercode = Boilerplate::new()
println!("{:?}", boilercode.csv);
boilercode.operations()
}
It's a little different then your current implementation, but I find this to be cleaner. And it also gives you the freedom to call your impl methods from others while keeping csv data in the instance. Like this:
impl Boilerplate {
// --snip--
fn process(&self) {
self.operations()
}
fn operations(&self) {
// work with self.csv
}
}
I am trying to implement custom derive macros for my traits, and they actually work!
However I have a slight problem. I can't seem to find a way to include generic parameters to the trait.
Specifically, I want to do something like this : #[derive(MyCustomDerive<'a, B, C>)]
Instead, right now I am hard-coding the generics, like so :
let gen = quote! {
impl #impl_generics Graph<'a, V, E> for #name #ty_generics #where_clause {
fn Map(&self) -> &MAP<V, E> {
&self.map
}
...
}
As you can see, I am including 'a, V and E fixed within the quote block, instead of something I want to achieve, which is being able to flexibly derive the trait with the generic types I want.
What I would like is something akin to this :
#[derive(MyCustomDerive<'a, B, C>)]
to result in something equivalent to this
let gen = quote! {
impl #impl_generics Graph<'a, B, C> for #name #ty_generics #where_clause {
fn Map(&self) -> &MAP<B, C> {
&self.map
}
...
}
This would allow me to reserve (of course if necessary) V and E for other things and in my opinion make code more controllable.
Thank you for your help!
Update 1 :
This is how my derive function looks
pub fn derive(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let generics = &ast.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let gen = quote! {
impl #impl_generics Graph<'a, V, E> for #name #ty_generics #where_clause {
fn Map(&self) -> &MAP<V, E> {
&self.map
} ...
I don't believe it's possible to use exactly the syntax you describe in your post (#[derive(MyCustomDerive<'a, B, C>)]). However, consider the following syntax that used an additional custom attribute instead:
#[derive(MyTrait)]
#[my_trait('a, B, C)]
struct MyStruct {
// ...
}
To allow the my_trait attribute to be used, you'll have to add an attributes section to your proc_macro_derive attribute.
#[proc_macro_derive(MyTrait, attributes(my_trait))]
pub fn derive_my_trait(input: TokenStream) -> TokenStream {
// ...
}
For help with parsing the attribute itself, take a look at syn::Attribute. The tokens field is a TokenStream from which you could extract the necessary parameters. For example, if your trait has one lifetime and two type parameters, your parsing logic might look something like this:
struct MyParams(syn::Lifetime, syn::Ident, syn::Ident);
impl syn::Parse for MyParams {
fn parse(input: syn::ParseStream) -> Result<Self> {
let content;
syn::parenthesized!(content in input);
let lifetime = content.parse()?;
content.parse::<Token![,]>()?;
let type1 = content.parse()?;
content.parse::<Token![,]>()?;
let type2 = content.parse()?;
Ok(MyParams(lifetime, type1, type2))
}
}
pub fn derive(ast: &syn::DeriveInput) -> TokenStream {
let attribute = ast.attrs.iter().filter(
|a| a.path.segments.len() == 1 && a.path.segments[0].ident == "my_trait"
).nth(0).expect("my_trait attribute required for deriving MyTrait!");
let parameters: MyParams = syn::parse2(attribute.tokens.clone()).expect("Invalid my_trait attribute!");
// ... do stuff with `parameters`
}
I am wrapping libxml2 in Rust as an exercise in learning the Rust FFI, and I have come across something strange. I am new to Rust, but I believe the following should work.
In main.rs I have:
mod xml;
fn main() {
if let doc = xml::parse_file("filename") {
doc.simple_function();
}
}
And xml.rs is:
extern create libc;
use libc::{c_void, c_char, c_int, c_ushort};
use std::ffi::CString;
// There are other struct definitions I've implemented that xmlDoc
// depends on, but I'm not going to list them because I believe
// they're irrelevant
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[repr(C)]
struct xmlDoc {
// xmlDoc structure defined by the libxml2 API
}
pub struct Doc {
ptr: *mut xmlDoc
}
impl Doc {
pub fn simple_function(&self) {
if self.ptr.is_null() {
println!("ptr doesn't point to anything");
} else {
println!("ptr is not null");
}
}
}
#[allow(non_snake_case)]
#[link(name = "xml2")]
extern {
fn xmlParseFile(filename: *const c_char) -> *mut xmlDoc;
}
pub fn parse_file(filename: &str) -> Option<Doc> {
unsafe {
let result;
match CString::new(filename) {
Ok(f) => { result = xmlParseFile(f.as_ptr()); },
Err(_) => { return None; }
}
if result.is_null() {
return None;
}
Some(Doc { ptr: result })
}
}
I'm wrapping the C struct, xmlDoc in a nice Rust-friendly struct, Doc, to have a clear delineation between the safe (Rust) and unsafe (C) data types and functions.
This all works for the most part except when I compile, I get an error in main.rs:
src/main.rs:38:13: 38:28 error: no method named 'simple_function' found
for type 'std::option::Option<xml::Doc>' in the current scope
src/main.rs:38 doc.simple_function();
^~~~~~~~~~~~~~~
error: aborting due to previous error`
It seems convinced that doc is an Option<xml::Doc> even though I'm using the if let form that should unwrap the Option type. Is there something I'm doing incorrectly?
match xml::parse_file("filename") {
Some(doc) => doc.simple_function(),
None => {}
}
The above works fine, but I'd like to use the if let feature of Rust if I'm able.
You need to pass the actual pattern to if let (unlike languages like Swift which special case if let for Option types):
if let Some(doc) = xml::parse_file("filename") {
doc.simple_function();
}
Say we want to have objects implementations switched at runtime, we'd do something like this:
pub trait Methods {
fn func(&self);
}
pub struct Methods_0;
impl Methods for Methods_0 {
fn func(&self) {
println!("foo");
}
}
pub struct Methods_1;
impl Methods for Methods_1 {
fn func(&self) {
println!("bar");
}
}
pub struct Object<'a> { //'
methods: &'a (Methods + 'a),
}
fn main() {
let methods: [&Methods; 2] = [&Methods_0, &Methods_1];
let mut obj = Object { methods: methods[0] };
obj.methods.func();
obj.methods = methods[1];
obj.methods.func();
}
Now, what if there are hundreds of such implementations? E.g. imagine implementations of cards for collectible card game where every card does something completely different and is hard to generalize; or imagine implementations for opcodes for a huge state machine. Sure you can argue that a different design pattern can be used -- but that's not the point of this question...
Wonder if there is any way for these Impl structs to somehow "register" themselves so they can be looked up later by a factory method? I would be happy to end up with a magical macro or even a plugin to accomplish that.
Say, in D you can use templates to register the implementations -- and if you can't for some reason, you can always inspect modules at compile-time and generate new code via mixins; there are also user-defined attributes that can help in this. In Python, you would normally use a metaclass so that every time a new child class is created, a ref to it is stored in the metaclass's registry which allows you to look up implementations by name or parameter; this can also be done via decorators if implementations are simple functions.
Ideally, in the example above you would be able to create Object as
Object::new(0)
where the value 0 is only known at runtime and it would magically return you an Object { methods: &Methods_0 }, and the body of new() would not have the implementations hard-coded like so "methods: [&Methods; 2] = [&Methods_0, &Methods_1]", instead it should be somehow inferred automatically.
So, this is probably extremely buggy, but it works as a proof of concept.
It is possible to use Cargo's code generation support to make the introspection at compile-time, by parsing (not exactly parsing in this case, but you get the idea) the present implementations, and generating the boilerplate necessary to make Object::new() work.
The code is pretty convoluted and has no error handling whatsoever, but works.
Tested on rustc 1.0.0-dev (2c0535421 2015-02-05 15:22:48 +0000)
(See on github)
src/main.rs:
pub mod implementations;
mod generated_glue {
include!(concat!(env!("OUT_DIR"), "/generated_glue.rs"));
}
use generated_glue::Object;
pub trait Methods {
fn func(&self);
}
pub struct Methods_2;
impl Methods for Methods_2 {
fn func(&self) {
println!("baz");
}
}
fn main() {
Object::new(2).func();
}
src/implementations.rs:
use super::Methods;
pub struct Methods_0;
impl Methods for Methods_0 {
fn func(&self) {
println!("foo");
}
}
pub struct Methods_1;
impl Methods for Methods_1 {
fn func(&self) {
println!("bar");
}
}
build.rs:
#![feature(core, unicode, path, io, env)]
use std::env;
use std::old_io::{fs, File, BufferedReader};
use std::collections::HashMap;
fn main() {
let target_dir = Path::new(env::var_string("OUT_DIR").unwrap());
let mut target_file = File::create(&target_dir.join("generated_glue.rs")).unwrap();
let source_code_path = Path::new(file!()).join_many(&["..", "src/"]);
let source_files = fs::readdir(&source_code_path).unwrap().into_iter()
.filter(|path| {
match path.str_components().last() {
Some(Some(filename)) => filename.split('.').last() == Some("rs"),
_ => false
}
});
let mut implementations = HashMap::new();
for source_file_path in source_files {
let relative_path = source_file_path.path_relative_from(&source_code_path).unwrap();
let source_file_name = relative_path.as_str().unwrap();
implementations.insert(source_file_name.to_string(), vec![]);
let mut file_implementations = &mut implementations[*source_file_name];
let mut source_file = BufferedReader::new(File::open(&source_file_path).unwrap());
for line in source_file.lines() {
let line_str = match line {
Ok(line_str) => line_str,
Err(_) => break,
};
if line_str.starts_with("impl Methods for Methods_") {
const PREFIX_LEN: usize = 25;
let number_len = line_str[PREFIX_LEN..].chars().take_while(|chr| {
chr.is_digit(10)
}).count();
let number: i32 = line_str[PREFIX_LEN..(PREFIX_LEN + number_len)].parse().unwrap();
file_implementations.push(number);
}
}
}
writeln!(&mut target_file, "use super::Methods;").unwrap();
for (source_file_name, impls) in &implementations {
let module_name = match source_file_name.split('.').next() {
Some("main") => "super",
Some(name) => name,
None => panic!(),
};
for impl_number in impls {
writeln!(&mut target_file, "use {}::Methods_{};", module_name, impl_number).unwrap();
}
}
let all_impls = implementations.values().flat_map(|impls| impls.iter());
writeln!(&mut target_file, "
pub struct Object;
impl Object {{
pub fn new(impl_number: i32) -> Box<Methods + 'static> {{
match impl_number {{
").unwrap();
for impl_number in all_impls {
writeln!(&mut target_file,
" {} => Box::new(Methods_{}),", impl_number, impl_number).unwrap();
}
writeln!(&mut target_file, "
_ => panic!(\"Unknown impl number: {{}}\", impl_number),
}}
}}
}}").unwrap();
}
The generated code:
use super::Methods;
use super::Methods_2;
use implementations::Methods_0;
use implementations::Methods_1;
pub struct Object;
impl Object {
pub fn new(impl_number: i32) -> Box<Methods + 'static> {
match impl_number {
2 => Box::new(Methods_2),
0 => Box::new(Methods_0),
1 => Box::new(Methods_1),
_ => panic!("Unknown impl number: {}", impl_number),
}
}
}
I see some Rust code that looks like this:
#![feature(default_type_params)]
After a lot of Googling, I get nothing about it. What is this? When should I use it?
It is enabling feature called default type parameters, but you already know that, so let me show you an example:
#![feature(default_type_params)]
struct Foo<A=(int, char)> { // default type parameter here!
a: A
}
fn default_foo(x: Foo) {
let (_i, _c): (int, char) = x.a;
}
fn main() {
default_foo(Foo { a: (1, 'a') })
}
Without default type parameters, you would need to explicitly set parameters:
struct Foo<A> {
a: A
}
fn default_foo(x: Foo<(int, char>)) {
let (_i, _c): (int, char) = x.a;
}
fn main() {
default_foo(Foo { a: (1, 'a') })
}
Example stolen from here: https://github.com/rust-lang/rust/pull/11217