Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
gonadic io
Feb 16, 2011

>>=

MALE SHOEGAZE posted:

I did some rust this weekend and I will feel pretty convinced that it's going to be a big deal.

I know you were all anxious to get my opinion so there it is.

Quick pick a top library from another language and then write it in Rust! Parsec, lens, an xml parser or something.

Adbot
ADBOT LOVES YOU

gonadic io
Feb 16, 2011

>>=
Does anybody want to critique my babby's first data structure? http://codepad.org/SG2ERyJn

There were a few things that I tried to do and failed. I know that the methods should probably be implemented in an impl with a &self parameter but I'm ignoring that for now.

gonadic io
Feb 16, 2011

>>=
Thanks very much, that's enough to get me started. I've decided to go back and finish the Rust introduction though, as I'm hoping things like into_iter will he explained by its end.

E: let's just say that I'm getting a renewed sense of the frustrations that people often feel when learning haskell

gonadic io fucked around with this message at 13:50 on Feb 19, 2016

gonadic io
Feb 16, 2011

>>=
It works!

https://play.rust-lang.org/?gist=3c137f16de010859622b&version=stable

And after only 2 and a half glasses of wine too!

This is step 1 on my plan for world domination writing my game in F#/Unity with the core data structures in Rust.

e: and now to implement ray casting... although I am cheating slightly because I've already implemented all of this in F# before I restarted in Scala and now I'm restarting again in Rust so I'm pretty familiar with the actual logic.

gonadic io fucked around with this message at 00:33 on Feb 20, 2016

gonadic io
Feb 16, 2011

>>=
Not that I've actually done it yet, but Rust's FFI can be used in place of a C++ external function as described by this blog post:
code:
// The plugin referenced by `DLLImport` must be compiled
// and placed in your project's Assets/Plugins/ folder.
// Make sure that you rename the lib<name>.dylib to
// lib<name>.bundle so that Unity can find it.

[DllImport("libunity_rust")]
private static extern int double_input(int x);
// call 'double_input' like any other function
so it doesn't seem that bad to do although anything involving the heap will probably be annoying. There's still lots of resources on the topic though.

And then F# in place of C# is trivial although I might need one or two C# wrappers if Unity demands C# in places? I'm not sure there either.

Basically this is a fun adventure where I throw myself into the deep end and learn some new technologies/languages! I've never used Rust before, nor Unity, the only non-GC'd language I've used before is 1-2 tiny toy C programs, and I haven't really done all that much in F# before.

I am however pretty good at Haskell :q:

gonadic io fucked around with this message at 00:32 on Feb 20, 2016

gonadic io
Feb 16, 2011

>>=
Is there a better way to get the first Some in a list of Options (and if there isn't any, return None) than this?
code:
let children : [(bool, bool, bool); 10] = ...;
let test_child : (bool, bool, bool) -> Option<Vec3<f32>> = ...;

children.into_iter()
        .map(|&(x, y, z)| test_child(x, y, z))
        .find(|x| x.is_some())
        .and_then(|x| x)
test_child is quite an expensive function so I want to calculate it lazily, i.e. only for the children that I need to. I had a look through the standard library but couldn't find anything like msum (as it's called in Haskell) or a more specialised variant in Iterator or Option's docs.

If anybody is interesting in my ongoing project, the ray casting functionality is done now. Also the file's reached 200 lines so I guess I should probably split it into modules and stuff. Any pointers or references are appreciated: https://gist.github.com/anonymous/224fcfb0e171941715a6

gonadic io fucked around with this message at 19:25 on Feb 21, 2016

gonadic io
Feb 16, 2011

>>=

What I have (map and find) works and is short-circuiting, I was just wondering if there was a cleaner solution like [].cat_maybes().head_option() or something.

gonadic io fucked around with this message at 20:16 on Feb 21, 2016

gonadic io
Feb 16, 2011

>>=
Today I wrote the line
code:
lazy_static! { static ref PLUGIN: Mutex<Cell<Option<UnityPlugin<'static>>>> = Mutex::new(Cell::new(None)); }
That's what I get for trying to port C++ bindings (that make heavy use of global mutable pointers) to Rust I suppose.

gonadic io
Feb 16, 2011

>>=
Is this a reasonable way to pass an optional value over a FFI boundary? Passing a (nullable) pointer and then having the C# code calling another function to free it seems like lots of pain.

code:
#[repr(C)]
pub struct BadOption<T : Sized> {
	is_some : bool,
	value : T
}
e: here's the context.

gonadic io fucked around with this message at 22:04 on Mar 13, 2016

gonadic io
Feb 16, 2011

>>=
I have a weird and pretty gross function that I can't help but think "surely there must be a better way!" about :

code:
pub enum SVO {
    Voxel { voxel_type: i32,
            external_id: Cell<Option<u32>> },
    Octants ([Box<SVO>; 8]) 
}

impl SVO {
    pub fn new_octants(octant_types: [i32; 8]) -> SVO { ... }

    fn subdivide_voxel<D>(&mut self, deregister_voxel: &D) where D : Fn(u32) {
        match *self {
            SVO::Voxel{ ref external_id, .. } => {
                for voxel_id in external_id.get() {
                    deregister_voxel(voxel_id)
                };
            },
            _ => panic!("subdivide_voxel called on a non-voxel!")
        }


        match *self {
            SVO::Voxel { voxel_type, .. } => {
                *self = SVO::new_octants([voxel_type, voxel_type, voxel_type, voxel_type,
                                          voxel_type, voxel_type, voxel_type, voxel_type])
            },
            _ => panic!("subdivide_voxel called on a non-voxel!")
        }
    }
}
This currently works (or at least compiles), and the reason that is has to be two match statements is that one value is captured by ref and one by value. However external_id can't be in scope when we match on voxel_type because we overwrite the contents of *self. Hence one match to capture external_id and then another to match voxel_type (but not to capture external_id).

As ever, the context is here.

gonadic io
Feb 16, 2011

>>=
Perfect, thanks.

e: is there a better way to represent once-assignable nullable variables than Cell<Option<_>>? The type allows for re-assignation but I will never do that. I suppose I could always just put a wrapper around it.

gonadic io fucked around with this message at 11:26 on Mar 19, 2016

gonadic io
Feb 16, 2011

>>=
Hi all, thanks loads for your previous help. I'm having another fight with the borrow checker again though.

In short, what I want to do is this:
code:
fn with_vec(vec: &mut Vec<u8>) {
    let octant_indices = octants.iter().map(|octant| {
        vec.add(???); // some operation that requires vec to be &mut
        vec.len() as u64 - 1
    });

    vec.add(???); // some operation that also requires vec to be &mut
}
And I'm getting the error "cannot borrow `*vec` as mutable because previous closure requires unique access [E0501]". I understand why I'm getting the error, as there are lots of mutable references to the same vec in scope and Mr B Checker is not a fan of that. I don't understand how to get around this though - the closure absolutely needs mutable access to the full vector and I absolutely need to be able to use it mutably afterwards.

Full context is here.

gonadic io fucked around with this message at 23:47 on Mar 22, 2016

gonadic io
Feb 16, 2011

>>=
Yep, that was exactly it thanks.

So even though the closure is lexically out of scope, because the iterator hasn't been evaluated yet the compiler still considers it to be in scope for the purposes of borrowing?

E: I think I get it now - the iterator borrows the reference because the closure does, and the iterator is still in scope (regardless of how much it was evaluated) which is causing the problem.

gonadic io fucked around with this message at 10:24 on Mar 23, 2016

gonadic io
Feb 16, 2011

>>=
Are people happy for me to continue asking this stuff here? I don't want to dominate the thread, I guess I could go to the terrible programmers yospos thread or the rust IRC instead.

Speaking of, I'm running into a situation with the FFI that I'm pretty sure is down to me using it incorrectly. My Rust code looks like:
code:
#[no_mangle]
pub extern "stdcall" fn svo_create
    (voxel_type: i32,
     register_voxel_extern: extern "stdcall" fn(Vec3<f32>, i32, i32) -> u32,
     deregister_voxel_extern: extern "stdcall" fn(u32)
    ) -> *mut ExternalSVO<'static> {

    ...

    println!("test deregister start");
    deregister_voxel_extern(0);
    println!("test deregister end");

    let deregister_voxel = &|external_id| {
        println!("rust callback deregister_voxel {}", external_id);
        deregister_voxel_extern(external_id);
        println!("rust callback deregister_voxel done");
    };

    ...

    let external_svo = ExternalSVO {
        register_voxel: register_voxel,
        deregister_voxel: deregister_voxel,
        svo: SVO::new_voxel(voxel_data, uid)
    };
    unsafe { transmute(Box::new(external_svo)) }
}
And the idea is for those callbacks to be able to be called given a pointer to this object. The callback itself (in this function) is fine, and the logs contain
test deregister start
from unity deregistering 0
test deregister end


However in other FFI functions, i.e.
code:
#[no_mangle]
pub extern "stdcall" fn svo_set_block(svo_ptr: *mut ExternalSVO, index_ptr: *const u8,
        index_len: usize, new_voxel_type: i32) {
    let mut svo_ref: &mut ExternalSVO = unsafe { &mut *svo_ptr };
    let index: &[u8] = unsafe { slice::from_raw_parts(index_ptr, index_len) };
    println!("about to deregister in other function");
    (svo_ref.deregister_voxel)(0);
    println!("deregistered in other function");
    let voxel_data = VoxelData::new(new_voxel_type);
    svo_ref.svo.set_block(&svo_ref.deregister_voxel, &svo_ref.register_voxel, index, voxel_data);
}
When this gets called the log looks like:
about to deregister in other function
rust callback deregister_voxel 0
<end of log>


So from what I can see the callback works fine when called initially, but when the address is called again later it crashes with
Thread 0 Crashed:: CrBrowserMain Dispatch queue: com.apple.main-thread
0 ??? 0x00007fff5fbfc860 0 + 140734799792224
1 libcarved_rust.bundle 0x000000011eeef77c svo_set_block + 28


I must say that debugging FFI is really quite frustrating because Unity just crashes with no message, the "send to Apple?" window contains the message above and then the Unity log contains the tracing statements.

So how should I keep these functions around and allow them to be called just given the pointer to the ExternalSVO.

Full context (including the definition of ExternalSVO) is on my github.

gonadic io fucked around with this message at 20:40 on Mar 27, 2016

gonadic io
Feb 16, 2011

>>=
I moved the closure to the internal function like so:

code:
#[repr(C)]
pub struct ExternalSVO {
    pub register_voxel_extern: extern "stdcall" fn(Vec3<f32>, i32, VoxelData) -> u32,
    pub deregister_voxel_extern: extern "stdcall" fn(u32),
    pub svo: SVO
}

#[no_mangle]
pub extern "stdcall" fn svo_set_block(svo_ptr: *mut ExternalSVO, index_ptr: *const u8, index_len: usize, new_voxel_type: i32) {
    let &mut ExternalSVO { ref mut svo, register_voxel_extern, deregister_voxel_extern } = unsafe { &mut *svo_ptr };
    let index: &[u8] = unsafe { slice::from_raw_parts(index_ptr, index_len) };
    let voxel_data = VoxelData::new(new_voxel_type);

    let r = &|vec, depth, data| register_voxel_extern(vec, depth, data);
    let d = &|id| deregister_voxel_extern(id);
    svo.set_block(d, r, index, voxel_data);
}
And it's all good now, as the closure hasn't been dropped when set_block is called.

I tried a few times without the intermediate (seemingly no-op) closure there by casting the extern "stdcall" fn to both &Fn(...) -> u32 and Box<Fn(...) -> u32> and then passing them directly to set_blocks which expects a &Fn but neither worked.

I got either
carved_rust.rs:43:13: 43:35 error: the trait `core::ops::Fn<(nalgebra::structs::vec::Vec3<f32>, i32, svo::VoxelData)>` is not implemented for the type `extern "stdcall" fn(nalgebra::structs::vec::Vec3<f32>, i32, svo::VoxelData) -> u32` [E0277]
or
carved_rust.rs:45:9: 45:44 error: the trait `core::ops::Fn<(nalgebra::structs::vec::Vec3<f32>, i32, svo::VoxelData)>` is not implemented for the type `Box<core::ops::Fn(nalgebra::structs::vec::Vec3<f32>, i32, svo::VoxelData) -> u32>` [E0277]

Is the Fn trait implemented for extern "stdcall" fn? Or is it something to do with the missing "-> u32"?

gonadic io
Feb 16, 2011

>>=

Vanadium posted:

(fwiw people tend to pass closures by value when possible and I don't recall having seen &|...|)

The reason I'm doing this is that inside set_blocks they get cloned and passed around a whole bunch and so references are needed. I have at least made it so that the public function takes them by value.

Honestly I'm hoping that the compiler will optimise away passing the references to every invocation of set_voxel_from. The way I'd do this in Haskell would be to define the sub-function as a closure inside the top-level function which just uses the register and deregister functions in scope but apparently in Rust you can't define recursive closures or fns inside another fn.
code:
pub trait Register: Fn(Vec3<f32>, i32, VoxelData) -> u32 {} // No trait aliases :(
impl<R: Fn(Vec3<f32>, i32, VoxelData) -> u32> Register for R {}

pub trait Deregister: Fn(u32) {}
impl<D: Fn(u32)> Deregister for D {}

impl SVO {
    pub fn set_block<R: Register, D: Deregister>(&mut self, register_voxel: R, deregister_voxel: D,
                           index: &[u8], new_voxel_data: VoxelData) {
        self.set_voxel_from(&register_voxel, &deregister_voxel, index, new_voxel_data, zero(), 0);
    }

    fn set_voxel_from<R: Register, D: Deregister>(&mut self, register_voxel: &R, deregister_voxel: &D,
    	                    index: &[u8], new_voxel_data: VoxelData, origin: Vec3<f32>, depth: i32) {
        ...
    }
}
e: I guess I could just make a little private SetBlockEnvironment struct and pass references to that to the sub-function? That'd certainly be cleaner I suppose.

gonadic io fucked around with this message at 11:00 on Mar 28, 2016

gonadic io
Feb 16, 2011

>>=
I have lot of fixed sized arrays. Functions that return [T; 8] etc.

Working with them is a real pain since neither FromIterator nor ToIterator are implemented for them. I feel that things like map and collect should be available but it's pretty cumbersome converting between slices in order to use these functions.

Is there a recommended way for working with arrays rather than slices or should I not bother?

e: for a concrete example I have

code:
impl SubImage {
    fn split_threshold(&self) -> Option<[SubImage; 2]>
    fn quads(&self) -> Option<[SubImage; 4]>
}
and I want to implement
code:
impl SubImage {
    pub fn octs(&self) -> Option<[SubImage; 8]>
}
and the order that quads/split_threshold are called in on a SubImage doesn't matter.

I tried at first to use some flat_maps, but couldn't figure that out and instead am now trying to do it explicitly with pattern matching but still haven't managed yet. Is there a nice way to do this? Should I just use slices and forgo the compile-time guarantees?

gonadic io fucked around with this message at 20:18 on Apr 4, 2016

gonadic io
Feb 16, 2011

>>=
Presented without comment:
code:
let sum = (self.x_0 .. self.x_n).map(|x| {
    ...
}).fold(0u32, |x, y| x+y); // Dear Rust, gently caress you.
Yes I could use nightly and a feature gate, but I quite like my code to not break between releases.

gonadic io
Feb 16, 2011

>>=

gonadic io posted:

code:
impl SubImage {
    fn split_threshold(&self) -> Option<[SubImage; 2]>
    fn quads(&self) -> Option<[SubImage; 4]>
}

I had a chance to try and combine these two functions tonight and am still fighting the borrow checker. SubImage implements Copy and Clone, so why I'm getting reference not living long enough errors even when I tried to clone() everything is beyond me. I get that quads goes out of scope at the end of the closure so any references to it would be invalid, but I'm copying all of the SubImages right? Making it a move closure doesn't help either.

Honestly I feel that this entire thing could be done as a few flat_maps but I've yet to get it to compile even 1) doing a bunch of manual indexing and 2) pretending that split_threshold can never fail.

code:
impl SubImage {
	pub fn octs(&self) -> Option<[SubImage; 8]> {
		self.quads().and_then(|quads| {
			let octs01 = quads[0].split_threshold().unwrap();
			let octs23 = quads[1].split_threshold().unwrap();
			let octs45 = quads[2].split_threshold().unwrap();
			let octs67 = quads[3].split_threshold().unwrap();
			Some([octs01[0], octs01[1],
			      octs23[0], octs23[1],
			      octs45[0], octs45[1],
			      octs67[0], octs67[1]])
		})
	}
}
error: `quads[..]` does not live long enough
reference must be valid for the anonymous lifetime #1 defined on the block at 63:45... (the entire function octs)
...but borrowed value is only valid for the scope of parameters for function at 64:37 (the closure)

gonadic io
Feb 16, 2011

>>=
I see now, it's because SubImage contains a reference to a byte buffer.

code:
struct SubImage<'a> {image: & 'a [u8] } 
(as well as a bunch of u32s tracking the start and end of the sub images)

I tried adding 'a lifetimes everywhere to all the references and SubImage but I guess that was being done implicitly by the compiler anyway.

e: when I next get internet I'll push to github so you can see the whole code

e2: https://github.com/djmcgill/carved/blob/master/carved_rust/src/svo/generator/height_map.rs

gonadic io fucked around with this message at 09:44 on Apr 8, 2016

gonadic io
Feb 16, 2011

>>=
That makes perfect sense, thanks all. I'll sort it out tonight.

Of course, given how much of a pain using map with arrays is I might just stick with my manual indexing. That or write a map_8 function.

gonadic io
Feb 16, 2011

>>=
I own a [T; 8] and would like to access the Ts out of it without copying (this is important). Then I'd like to call a FnOnce(T) -> T on each one and package them back up in an array.

I managed to cobble together a working version thanks to SO:
code:
impl SVO<Registered> {
    fn deregister<D: Deregister>(self, deregister_voxel: &D) -> SVO<Unregistered> {
        match self {
            SVO::Voxel { registration: Registered { external_id }, data } => {
                deregister_voxel(external_id);
                SVO::Voxel { data: data, registration: Unregistered }
            },
            SVO::Octants (mut octants) => {
                let new0 = unsafe { mem::replace(&mut octants[0], mem::uninitialized()).deregister(deregister_voxel) };
                let new1 = unsafe { mem::replace(&mut octants[1], mem::uninitialized()).deregister(deregister_voxel) };
                let new2 = unsafe { mem::replace(&mut octants[2], mem::uninitialized()).deregister(deregister_voxel) };
                let new3 = unsafe { mem::replace(&mut octants[3], mem::uninitialized()).deregister(deregister_voxel) };
                let new4 = unsafe { mem::replace(&mut octants[4], mem::uninitialized()).deregister(deregister_voxel) };
                let new5 = unsafe { mem::replace(&mut octants[5], mem::uninitialized()).deregister(deregister_voxel) };
                let new6 = unsafe { mem::replace(&mut octants[6], mem::uninitialized()).deregister(deregister_voxel) };
                let new7 = unsafe { mem::replace(&mut octants[7], mem::uninitialized()).deregister(deregister_voxel) };
                SVO::Octants([
                    Box::new(new0), Box::new(new1), Box::new(new2), Box::new(new3),
                    Box::new(new4), Box::new(new5), Box::new(new6), Box::new(new7)
                ])
            }
        }
    }
}
Which is functioning, if ugly. Note that SVO does NOT implement Drop so I'm fine (I think) just messing with their memory.

However I have the function:
code:
impl<R: RegistrationTrait> SVO<R> {
    pub fn new_octants<F: Fn(u8) -> SVO<R>>(make_octant: F) -> SVO<R> { ... }
}
which I'd like to use to make this much nicer, but when I try to package up the repeated code inside the closure like so:
code:
let fun: &Fn(u8) -> SVO<_> = &|ix: u8| unsafe { mem::replace(&mut octants[ix as usize], mem::uninitialized()).deregister(deregister_voxel) };
SVO::new_octants(fun)
(the trait object is just to convince Rust to give a useful type error, otherwise I just get "Fn(u8) is not implemented for [closure]"). Note too that this has to (I'm pretty sure) be a Fn and not a FnOnce as it'll get called on each of the 8 octants in new_octants.

I get the error
error: cannot borrow data mutably in a captured outer variable in an `Fn` closure [E0387].

Is there any way I can 1) bypass needing a mutable pointer to an outer variable in the closure, or 2) have a mutable pointer to an outer variable in the closure?

Full context is here: https://github.com/djmcgill/carved/blob/master/carved_rust/src/svo/mod.rs

gonadic io fucked around with this message at 19:44 on Apr 20, 2016

gonadic io
Feb 16, 2011

>>=
It might well entirely be that I'm trying to do the wrong thing here too.

The core problem is that I have a large tree of nested box pointers to SVO<Registered>, and I'd like to end with SVO<Unregistered>. Given that this is a potentially huge structure I'd like to do it with as little copying as possible so I've got deregister taking self by value as I'll never want to access the old SVO.

The leaf case is easy, but given the array [Box<SVO<Registered>>; 8] I'd like to call deregister on each of them recursively (to get 8 Box<SVO<Unregistered>>s) and then collect them up. Given that it's only 8 pointers I'm not that fussed about reusing the old array's memory (but I suppose if I can I will).

What I was asking in my post was how to avoid just manually indexing 8 times but if my approach is wrong I'd like to know how I could do it better!.

However, changing new_octants to require a FnMut rather than a Fn seems to work perfectly.

This is what I've got now, does it make sense? Does having the index be u8 actually help at all? The index can only be 0-7 so I kind of would like a u3 type so that invalid values can't be represented but then I always end up converting to usize to do any actual indexing.
code:
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Registered { pub external_id: u32 }
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Unregistered;

pub trait RegistrationTrait {}
impl RegistrationTrait for Registered {}
impl RegistrationTrait for Unregistered {}

impl<R: RegistrationTrait> SVO<R> {
    pub fn new_octants<F: FnMut(u8) -> SVO<R>>(mut make_octant: F) -> SVO<R> {
        SVO::Octants([
            Box::new(make_octant(0)), Box::new(make_octant(1)),
            Box::new(make_octant(2)), Box::new(make_octant(3)),
            Box::new(make_octant(4)), Box::new(make_octant(5)),
            Box::new(make_octant(6)), Box::new(make_octant(7))])
    }
}

pub trait Deregister: Fn(u32) {}

impl SVO<Registered> {
    fn deregister<D: Deregister>(self, deregister_voxel: &D) -> SVO<Unregistered> {
        match self {
            SVO::Voxel { registration: Registered { external_id }, data } => {
                deregister_voxel(external_id);
                SVO::Voxel { data: data, registration: Unregistered }
            },
            SVO::Octants (mut octants) =>
                SVO::new_octants(|ix: u8| unsafe {
                    mem::replace(&mut octants[ix as usize], mem::uninitialized()).deregister(deregister_voxel)
                })
        }
    }
}

gonadic io fucked around with this message at 10:03 on Apr 21, 2016

gonadic io
Feb 16, 2011

>>=
What about the best of both worlds?

code:
pub struct Registered { external_id: u32 };
pub struct Unregistered { _dummy: u32 };

enum SVO<R: RegistrationTrait> {
    Voxel {
        registration: R,
        data: VoxelData
    },
    Octants([Box<SVO<R>>; 8]),
}
Now we can still transmute in SVO::register and SVO::deregister, but still get the type-safety of not being able to access external_id in Unregistered voxels.

gonadic io
Feb 16, 2011

>>=
I'm having quite a lot of trouble with what the types of closures are. I have

code:
use svo::voxel_data::VoxelData;
use nalgebra::Vec3;
use std::marker::PhantomData;

// Sadly trait aliasing doesn't exist yet.
pub trait Register: Fn(Vec3<f32>, i32, VoxelData) -> u32 {}
impl<T: Fn(Vec3<f32>, i32, VoxelData) -> u32> Register for T {}
pub trait Deregister: Fn(u32) {}
impl<T: Fn(u32)> Deregister for T {}

#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Registered { pub external_id: u32 }
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Unregistered { pub _padding: u32 }
impl Unregistered {
    pub fn new() -> Unregistered { Unregistered { _padding: 0 } }
}

pub trait RegistrationTrait {}
impl RegistrationTrait for Registered {}
impl RegistrationTrait for Unregistered {}

pub struct RegistrationFunctions<T: RegistrationTrait, R: Register, D: Deregister> {
    pub register: Box<R>,
    pub deregister: Box<D>,
    registration_trait: PhantomData<T>
}

impl<R: Register, D: Deregister> RegistrationFunctions<Unregistered, R, D> {
	pub fn blank_registration_functions() -> RegistrationFunctions<Unregistered, R, D> {
		let register_closure = |_: Vec3<f32>, _: i32, _: VoxelData| 0;
		let dereg_closure = |_: u32| {};
		RegistrationFunctions {
			register: Box::new(register_closure),
			deregister: Box::new(dereg_closure),
			registration_trait: PhantomData

		}
	}
}
And the idea is to (eventually) have two ways to construct a RegistrationFunctions struct - either a dummy one for Unregistered, or one has a constructor that takes two external functions to make a Registered one. This way it's possible to construct SVOs polymorphically without caring if they've been registered or not.

My current error is:
code:
expected `svo::registration::RegistrationFunctions<svo::registration::Unregistered, R, D>`,
    found `svo::registration::RegistrationFunctions<_, [closure], [closure]
But I think in general I'm getting quite confused with how function types work. Do I need to be polymorphic here? "impl<R: Register, D: Deregister> RegistrationFunctions<Unregistered, R, D>"

Also I tried to make the functions inside the RegistrationFunctions object be references instead of on the heap but apparently the references to the Fn types might outlive the Fns themselves? Do Fns have a hidden lifetime parameter?

e: to recall, Unregistered has the _padding parameter so that I can potentially transmute between Registered and Unregistered.

gonadic io
Feb 16, 2011

>>=
I reread the closure tutorial and finally grokked the comment about how closures are implemented by the compiler constructing bespoke structs for each one and then implementing the Fn traits.

So after just Boxxing everything, I ended up with
code:
pub struct RegistrationFunctions {
    pub register: Box<Fn(Vec3<f32>, i32, VoxelData) -> Registered>,
    pub deregister: Box<Fn(Registered)>
}

impl RegistrationFunctions {
	pub fn dummy() -> RegistrationFunctions {
		RegistrationFunctions {
			register: Box::new(|_, _, _| Registered { external_id: 0}),
			deregister: Box::new(|_| {})
		}
	}

	pub fn external(
			ext_register: RegisterExtern,
		    ext_deregister: DeregisterExtern) -> RegistrationFunctions {
		RegistrationFunctions {
			register: Box::new(move |origin, depth, data|
				Registered{ external_id: ext_register(origin, depth, data) }),
			deregister: Box::new(move |Registered{external_id}| ext_deregister(external_id))
		}
	}
}
Which works great. I'm not super enthusiastic about allocating 2 closures on the heap for the dummy even though it's completely unnecessary but it's good enough for now.

gonadic io
Feb 16, 2011

>>=

Actually this didn't quite work, and the Boxes required lifetime parameters like so: (otherwise the closures were given static lifetimes)

code:
pub struct RegistrationFunctions<'a> {
    pub register: Box<Fn(Vec3<f32>, i32, VoxelData) -> Registered + 'a>,
    pub deregister: Box<Fn(Registered) + 'a>
}

gonadic io fucked around with this message at 16:10 on May 22, 2016

gonadic io
Feb 16, 2011

>>=
I'm actually running into a real design issue here around FFI:
I'd like to deregister a SVO, changing it's type
code:
impl SVO<Registered> {
    fn deregister(self, registration_fns: &RegistrationFunctions) -> SVO<Unregistered> {
        self.deregister_helper(registration_fns);
        unsafe { mem::transmute(self) }
    }
}
Thus any function that calls deregister must own the SVO, so the signature is
code:
impl SVO<Registered> {
    pub fn set_block(
        self,
        registration_fns: &RegistrationFunctions,
        index: &[u8],
        new_data: VoxelData);
}
But during the FFI, the external opaque pointer is (essentially) a *mut SVO<Registered> and so it seems to be impossible to call set_block on it. Should I just get rid of the type parameter to SVO (having set_blocks take a mutable reference) and just do my best to never forget to register one? I'm not entirely sure how to combine the two concepts and I certainly don't want to deep-copy the SVO in order to change its type.

gonadic io
Feb 16, 2011

>>=
Here's what I use:
code:
macro_rules! get(
	($e:expr) => (match $e { Some(e) => e, None => return None })
);

macro_rules! guard(
	($e:expr) => (if !$e { return None })
);

gonadic io
Feb 16, 2011

>>=

MALE SHOEGAZE posted:

boo! I tried that but I was doing parse_from_bytes<MyMessageType>(m). The parse errors probably should have clued me in.

And yeah, I'm still getting used to error handling, but I recognize that by just calling unwrap, I'm failing to handle errors.

I'm pretty sure all Rust I've ever written contains ".unwrap() // TODO" and all haskell "fromJust x -- TODO"

gonadic io
Feb 16, 2011

>>=

Asymmetrikon posted:

I think my least favorite thing about refactoring so far has been trying to isolate parts of a chain of iterators. The result of a .map().filter().whatever() has a gnarly result type.

Will 'impl trait' help for this case?

gonadic io
Feb 16, 2011

>>=

Asymmetrikon posted:

Yeah. Is that out of nightly yet?

Nope! And with good reason, there's still plenty of edge cases and bugs that are falling out. The main functionality works fine though.

Dehumanise yourself and face to nightly.

Or just make extensive use of type inference, either adding no explicit type sig, or use lots of underscores in it.

gonadic io
Feb 16, 2011

>>=
On the plus side I got rust's cross-compiling intrinsics working. On the downside now I have this in my code:

Rust code:
 // Extern definitions
extern {
    static mut __etext: usize;
    static mut __data_start__: usize;
    static mut __data_end__: usize;
    static mut __bss_start__: usize;
    static mut __bss_end__: usize;
}
...
// Inside the init function
let data_size = & __data_end__ as *const usize as usize - & __data_start__ as *const usize as usize; 
ptr::copy_nonoverlapping(& __etext as *const usize, &mut __data_start__ as *mut usize, data_size);

let bss_size = & __bss_end__ as *const usize as usize - & __bss_start__ as *const usize as usize; 
ptr::write_bytes(&mut __bss_start__ as *mut usize, 0, bss_size);
Is there a better way to get the addresses of variables? Or the size of the range between two address/pointers? I checked and the core pointer types don't implement any arithmetic operators.

gonadic io fucked around with this message at 22:30 on Jan 28, 2017

gonadic io
Feb 16, 2011

>>=

ShoulderDaemon posted:

If those are symbols coming from your linker script, they shouldn't have the type "usize", by the way. "()" is way more appropriate, because there's no data backing them.

In the C code I'm looking at they're all defined "extern unsigned int" but I suppose that's just bad practice.

I figured out a way to take advantage of the auto-coerce:
code:
fn ptr_diff<T, U>(ptr_1: *const T, ptr_2: *const U) -> usize {
    ptr_1 as usize - ptr_2 as usize
}
...
let data_size = ptr_diff(& __data_end__, & __data_start__); 
ptr::copy_nonoverlapping(& __etext, &mut __data_start__, data_size);

let bss_size = ptr_diff(& __bss_end__, & __bss_start__); 
ptr::write_bytes(&mut __bss_start__, 0, bss_size);

gonadic io
Feb 16, 2011

>>=
"static mut __etext: ();" triggers
code:
warning: found Rust tuple type in foreign module; consider using a struct instead, #[warn(improper_ctypes)] on by default
After some research, the canonical Rust way to produce a zero-sized type for use in FFI is "enum Foo {}": https://internals.rust-lang.org/t/pre-rfc-opaque-structs/4005

For now honestly I think that I'm just going to disable that warning for those definitions as, unlike other sizes of tuples, the memory layout of () is guaranteed.

gonadic io fucked around with this message at 10:58 on Jan 29, 2017

gonadic io
Feb 16, 2011

>>=
I use Visual Studio Code and I like it a lot. It has support for auto-builds, syntax highlighting, error underlining, name lookup, autocomplete (some amount anyway), formatting code and stuff.

No semantic rename yet though sadly.
https://github.com/saviorisdead/RustyCode

e: if you want something leaner and are willing to put up with 1) nagging to pay, and 2) much worse rust support (but still syntax highlighting and goto definition) then there's always sublime

gonadic io fucked around with this message at 23:14 on Jan 31, 2017

gonadic io
Feb 16, 2011

>>=
Hmm, I'm running into a problem that isn't complex in itself but I'm sure that my solution is really unidiomatic. I'm continuing with my rust-for-arduino project and my RTC alarm enum looks like this

code:
pub enum RtcMode2Alarm {
    OFF, // Never
    SS { // Every Minute
        second: u8
    },
    MMSS { // Every Hour
        minute: u8, second: u8
    },
    HHMMSS { // Every Day
        hour: u8, minute: u8, second: u8
    },
    ...
}
And the goal is to set all of the relevant bits in the alarm match and then set the type of alarm.

Since Rust enums aren't fully-fledged objects that can have static members for each variant like Scala, and if-let blocks don't allow multiple patterns, and I don't really want to write and implement a HasSeconds/HasMinutes trait for this single method I went with this approach (which I don't really like):

code:
        let mut bits = 0u32;
        match alarm {
            SS { second } |
            MMSS { second, .. } |
            HHMMSS { second, .. } |
            DHHMMSS { second, .. } => {
                if second >= (1 << 6) { panic!("{:?} is too large, maximum 6 bits", second); }
                bits |= (second as u32) << 0; // The shifting isn't needed for second, but it is for the other fields
            },
            _ => {}
        }
        ... // do the same for minute, hour, day, month, and year

        self.ALARM.write(bits);
        self.wait_for_sync();

        let mask_val: u8 = match alarm {
            SS {..} => RTC_MODE2_MASK_SEL_SS_VAL,
            ...,
        };

        self.MASK.write(mask_val);
        self.wait_for_sync();
Any suggestions for a better way to do this?
Full snippet is here: https://gist.github.com/djmcgill/91556f58d1aedff2adc7428a6443aac2
Full code is here: https://github.com/djmcgill/to-the-moon/blob/master/src/component/rtc/mod.rs#L142

gonadic io
Feb 16, 2011

>>=
That is much better thanks, although making a new file and enum for every method is going to bloat my code somewhat...
I went with the explicit approach over the recursive macro for maintenance /ease of understanding purposes.

Next question:
I need to be able to attach a callback to the alarm interrupt. The way this is done in the C is to have a global mutable function pointer. When the alarm goes off if the pointer isn't null then execute the function.

I think I can use a static mut AtomicPtr<&'static Fn() > for this. I can't use Mutex or lazy_static because they both depend on std.

Amy better ways present themselves?

e: hmm, I could at least make the pointer private and the callback a parameter to the set_alarm method.

gonadic io fucked around with this message at 12:45 on Mar 10, 2017

gonadic io
Feb 16, 2011

>>=
Is there a way to generate an identifier inside a macro?

I have a macro that creates a struct that implements a trait (similar to closure structs implementing Fn)

code:
pub trait EightSegment {
    unsafe fn new() -> Self;
    fn display(&mut self, count: u32);
}

macro_rules! create_eight_segment (
    ($struct_name: ident,
     ...
    ) => {{
        pub struct $struct_name;
        impl EightSegment for $struct_name {
            ...
        }
        $struct_name::new()
    }}
);
Which is called like
code:
fn main() {
  let mut digit = unsafe {
    create_eight_segment!(EightSegment2, D7, D6, D3, D4, D5, D8, D9, D2)
  };
  loop {
    let s = ...
    digit.display(s);
  }
}
But I don't like that you have to explicitly specify the name of the struct. Is there a way to generate some name that you can never refer to, like how closure objects work?

Full code is here.

gonadic io fucked around with this message at 16:30 on Mar 22, 2017

Adbot
ADBOT LOVES YOU

gonadic io
Feb 16, 2011

>>=

Ralith posted:

Given that rust macros are intended to be hygenic, what happens if you just give the struct a fixed identifier directly? If it works at all I imagine it'll be scoped to the macroexpansion.

That worked perfectly actually, thanks. Also because the struct is created inside the macro body expression anyway:
code:
let mut digit = unsafe {
    create_eight_segment!(D7, D6, D3, D4, D5, D8, D9, D2)
  };
There's no way for the scope to clash at all. Thanks to macros I can even name those arguments like "PIN_1 = D7" in a way that I couldn't with regular functions!

edit: is it best practice to have the expansion be
code:
let mut digit = unsafe {
    struct Foo;
    impl Butt for Foo {}
    Foo::new()
  }
I'm still not sure how this would affect how that block actually executes at run-time. Would all the type definitions and impls happen at compile-time so at run-time that's identical to just having the "Foo::new()" on its own?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply