Can you call a trait static method implemented by types from another trait static method implemented in the trait? For example:
trait SqlTable {
fn table_name() -> String;
fn load(id: i32) -> Something {
...
Self::table_name() // <-- this is not right
...
}
}
This is now working thanks to Chris and Arjan (see comments/answers below)
fn main() {
let kiwibank = SqlTable::get_description(15,None::<Account>);
}
trait SqlTable {
fn table_name(_: Option<Self>) -> String;
fn get_description(id: i32, _: Option<Self>) -> String {
println!("Fetching from {} table", SqlTable::table_name(None::<Self>) );
String::from_str("dummy result")
}
}
struct Account {
id: i32,
name: String,
}
impl SqlTable for Account {
fn table_name(_: Option<Account>) -> String { String::from_str("account") }
}
You have to change Self to SqlTable:
trait SqlTable {
fn table_name() -> String;
fn load(id: i32) -> Self {
...
SqlTable::table_name() // <-- this is not right
...
}
}
Static methods are always called on a trait like SomeTrait::some_method(). Bug #6894 covers this issue.
Yes, you can call a trait static method [implemented by types] from another trait static method [implemented in the trait].
Static methods are always called on a trait like SomeTrait::some_method().
Where there is no Self or self in [a trait] function signature, it is not callable at present. The standard workaround until UFCS comes is to take an argument _: Option<Self> and pass it None::<T>.
See original question for code that (as of today) compiles.
This works without any workarounds with current Rust (verified with 1.65.0):
trait Trait {
fn x() -> i32 {
1
}
}
struct Struct {
y: i32,
}
impl Struct {
fn new(y: i32) -> Self {
Self { y }
}
fn f(self) -> i32 {
Self::x() + self.y
}
}
impl Trait for Struct {}
fn main() {
assert_eq!(Struct::new(1).f(), 2);
}
Related
I have implemented a struct inside a module. I am trying to implement the io::Read trait for a reference slice of the struct.
Here is a simplified version of my code. For now the new trait implementation is inefficient. I am trying different things to make it work.
struct mytype {
inner: f32,
}
impl io::Read for mytype {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
buf.write_u32::<LittleEndian>(self.inner.to_bits()).unwrap();
Ok(4)
}
}
impl<'a> io::Read for &'a [mytype] {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut vec8: Vec<u8> = vec![];
for t in self.iter() {
match (*t).read_u32::<LittleEndian>() {
Ok(u) => vec8.write_u32::<LittleEndian>(u).unwrap(),
Err(x) => (),
}
}
for (ind, it) in vec8.iter().enumerate() {
buf[ind] = *it;
}
Ok(vec8.len())
}
}
The rust compiler keeps complaining about the code with this error:
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> src\lib.rs:37:1
|
37 | impl<'a> io::Read for &'a [mytype] {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate
|
= note: the impl does not reference any types defined in this crate
= note: define and implement a trait or new type instead
Just implementing io::Read for mytype works correctly.
I'm trying to model a dataframe-like structure. I know how use enums here, but I'm exploring how do it similar to C#/Python/etc.
I tried to follow Rust Trait object conversion but things are not working:
use std::any::{Any};
use std::fmt::Debug;
pub trait Value: Any + Sized {
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
impl Value for i32 {}
#[derive(Debug)]
struct Frame {
data: Vec<Box<Any>>,
}
fn make_int(of: Vec<i32>) -> Frame {
let data = of.into_iter().map(|x| Box::new(x.as_any())).collect();
Frame {
data: data,
}
}
The compiler complains:
error[E0277]: the trait bound `std::vec::Vec<std::boxed::Box<std::any::Any>>: std::iter::FromIterator<std::boxed::Box<&std::any::Any>>` is not satisfied
--> src/main.rs:40:61
|
40 | let data = of.into_iter().map(|x| Box::new(x.as_any())).collect();
| ^^^^^^^ a collection of type `std::vec::Vec<std::boxed::Box<std::any::Any>>` cannot be built from an iterator over elements of type `std::boxed::Box<&std::any::Any>`
|
= help: the trait `std::iter::FromIterator<std::boxed::Box<&std::any::Any>>` is not implemented for `std::vec::Vec<std::boxed::Box<std::any::Any>>`
The main problem is with this function:
fn as_any(&self) -> &Any {
self
}
This means that you can borrow a Value as a &Any, (it converts a &Value into a &Any).
But then, you want to create a Box<Any> from that &Any. That will never work, because &Any is a borrowed value, while Box<Any> is owned.
The easiest solution would be to change the trait to return the boxed value (an owned trait object):
pub trait Value: Any + Sized {
fn as_boxed_any(&self) -> Box<Any> {
Box::new(self)
}
//The mut variation is not needed
}
Now the make_int function is trivial:
fn make_int(of: Vec<i32>) -> Frame {
let data = of.into_iter().map(|x| x.as_boxed_any()).collect();
Frame {
data: data,
}
}
UPDATE: Tinkering a bit, I've found that you can create the Vec<Box<Any>> by writing:
fn make_int(of: Vec<i32>) -> Frame {
let data = of.into_iter().map(|x| Box::new(x) as Box<Any>).collect();
Frame {
data: data,
}
}
If you are writing the trait only for this conversion, you don't actually need it.
I'm trying to provide a closure via a C callback using a static variable. I was able to get things working with a Fn type, but I'd like to make it work via FnMut to provide users of the library with more versatility.
Here's what I have:
lazy_static! {
static ref CALLBACK: Mutex<RefCell<Box<FnMut(Result<&str>) + Send>>> = Mutex::new(RefCell::new(Box::new(|_|())));
}
fn wrap_cb<F: Fn(Result<&str>)>(f: Option<F>) -> Option<unsafe extern "C" fn(*mut c_char, size_t)> {
match f {
Some(_) => {
unsafe extern "C" fn wrapped(msg: *mut c_char, len: size_t) {
let s = std::str::from_utf8(std::slice::from_raw_parts(msg as *const u8, len))
.map_err(Error::from);
let x = CALLBACK.lock().unwrap();
x.borrow_mut()(s);
}
Some(wrapped)
}
None => None,
}
}
This gives the error:
error[E0596]: cannot borrow immutable `Box` content as mutable
--> src/wpactrl.rs:56:17
|
56 | x.borrow_mut()(s);
| ^^^^^^^^^^^^^^ cannot borrow as mutable
It looks like the "cannot borrow immutable Box content as mutable" problem reduces to:
fn invoke(m: &Mutex<RefCell<Box<FnMut()>>>) {
let r = m.lock().unwrap();
r.borrow_mut()();
}
I haven't yet figured out why this works, but it does work if changed to:
fn invoke(m: &Mutex<RefCell<Box<FnMut()>>>) {
let r = m.lock().unwrap();
let f = &mut *r.borrow_mut();
f();
}
I want to create array like this:
let arr = [0; length];
Where length is a usize. But I get this error
E0307
The length of an array is part of its type. For this reason, this length
must be a compile-time constant.
Is it possible to create array with dynamic length? I want an array, not a Vec.
Is it possible to create array with dynamic length?
No. By definition, arrays have a length defined at compile time. A variable (because it can vary) is not known at compile time. The compiler would not know how much space to allocate on the stack to provide storage for the array.
You will need to use a Vec:
let arr = vec![0; length];
See also:
Is it possible to control the size of an array using the type parameter of a generic?
This should be possible after variable length arrays (VLA) are implemented.
You can create your own HeapArray. It's not that complicated if you read alloc's docs:
use std::alloc::{alloc, dealloc, Layout};
pub struct HeapArray<T> {
ptr: *mut T,
len: usize,
}
impl<T> HeapArray<T> {
pub fn new(len: usize) -> Self {
let ptr = unsafe {
let layout = Layout::from_size_align_unchecked(len, std::mem::size_of::<T>());
alloc(layout) as *mut T
};
Self { ptr, len }
}
pub fn get(&self, idx: usize) -> Option<&T> {
if idx < self.len {
unsafe { Some(&*(self.ptr.add(idx))) }
} else {
None
}
}
pub fn get_mut(&self, idx: usize) -> Option<&mut T> {
if idx < self.len {
unsafe { Some(&mut *(self.ptr.add(idx))) }
} else {
None
}
}
pub fn len(&self) -> usize {
self.len
}
}
impl<T> Drop for HeapArray<T> {
fn drop(&mut self) {
unsafe {
dealloc(
self.ptr as *mut u8,
Layout::from_size_align_unchecked(self.len, std::mem::size_of::<T>()),
)
};
}
}
impl<T> std::ops::Index<usize> for HeapArray<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
self.get(index).unwrap()
}
}
impl<T> std::ops::IndexMut<usize> for HeapArray<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
self.get_mut(index).unwrap()
}
}
You may also add methods like as_slice, get_unchecked, etc.
I am trying to have a struct which has a field which I assume should be of type Result<TempDir>. When I initialise an implementation of the field with new(), I would like that particular field to be initialised by the creation of a new temp directory. Later, I want to implement a method to read from that directory.
Here's the code, I am more worried about the syntax and proper use of libraries (why exactly are there over four libraries for read/write buffering in Rust, this is insane) as the logic should be right. Dont worry too much about the trait implementations, I just need directions in the syntax. Please don't be too harsh, as I know it doesn't compile, but with just two changes it should.
extern crate rustc_back;
use std::path::Path;
use std::fs::File;
use rustc_back::tempdir::TempDir as TempDir;
pub struct MyStorage {
temp_dir : Result<TempDir>
}
impl MyStorage {
pub fn new() -> MyStorage {
//tempo = match TempDir::new("encrypt_storage");
let store = match TempDir::new("encrypt_storage") {
Ok(dir) => dir,
Err(e) => panic!("couldn't create temporary directory: {}", e)
};
MyStorage { temp_dir: store }
//MyStorage { temp_dir: TempDir::new("encrypt_storage") }
}
}
impl Storage for MyStorage {
fn get(&self, name: Vec<u8>) -> Vec<u8> {
//let mut f = std::fs::File::open(self.temp_dir.path() / name);
let mut f = std::fs::File::open(&self.temp_dir){
// The `desc` field of `IoError` is a string that describes the error
Err(why) => panic!("couldn't open: {}", why.description()),
Ok(file) => file,
};
let mut s = String::new();
//f.read_to_string(&mut s);
match f.read_to_string(&mut s){
Err(why) => panic!("couldn't read: {}", why.description()),
Ok(_) => print!("contains:\n{}", s),
}
s.to_vec()
}
fn put(&mut self, name: Vec<u8>, data: Vec<u8>) {
// self.entries.push(Entry { name : name, data : data })
let mut f = File::create(self.temp_dir.path() / name);
f.write_all(data);
}
fn put(&mut self, name: Vec<u8>, data: Vec<u8>) {
// self.entries.push(Entry { name : name, data : data })
let mut f = File::create(self.temp_dir.path() / name);
f.write_all(data);
}
}
After fixing the indentation (Rust uses 4 spaces per level), removing the Storage for since you didn't provide that trait, removing commented-out code, and adding a main, you are left with this:
extern crate rustc_back;
use std::path::Path;
use std::fs::File;
use rustc_back::tempdir::TempDir as TempDir;
pub struct MyStorage {
temp_dir : Result<TempDir>
}
impl MyStorage {
pub fn new() -> MyStorage {
let store = match TempDir::new("encrypt_storage") {
Ok(dir) => dir,
Err(e) => panic!("couldn't create temporary directory: {}", e)
};
MyStorage { temp_dir: store }
}
}
impl MyStorage {
fn get(&self, name: Vec<u8>) -> Vec<u8> {
let mut f = std::fs::File::open(self.temp_dir.path() / name);
let mut s = String::new();
f.read_to_string(&mut s);
s.to_vec()
}
fn put(&mut self, name: Vec<u8>, data: Vec<u8>) {
let mut f = File::create(self.temp_dir.path() / name);
f.write_all(data);
}
}
fn main() {}
Compiling that has this error:
error: wrong number of type arguments: expected 2, found 1 [E0243]
temp_dir : Result<TempDir>
^~~~~~~~~~~~~~~
Which nicely points to the problematic type. Let's look at the docs for Result, which includes the definition:
pub enum Result<T, E> {
Ok(T),
Err(E),
}
So Result has two type parameters - T is used for the success case, and E is used for the failure case. Your code is only specifying one of them. My guess is that you looked at the docs for TempDir and copy-and-pasted:
fn new(prefix: &str) -> Result<TempDir>
However, if you click on the Result there, you'll see it goes to io::Result, which is simply a type alias that binds E to io::Error:
type Result<T> = Result<T, Error>;
With all that exposition out of the way, you can "fix" your problem by changing your MyStorage struct:
pub struct MyStorage {
temp_dir: std::io::Result<TempDir>,
}
And then you will get another compiler error, as you are already dealing with the Result via the match in MyStorage::new. You aren't storing a io::Result<TempDir>, you are just storing a TempDir! Changing your struct further:
pub struct MyStorage {
temp_dir: TempDir,
}
unlocks a whole new set of errors for you to figure out; but now you have gotten past that first hurdle!