I'd like to know the name of the function that called my function in Rust.
In C# there's CallerMemberName attribute which tells the compiler to replace the value of a string argument to which it's applied with the name of the caller.
Does Rust have anything like that?
I don't know of a compile time solution, but you can use the backtrace functionality to resolve it at runtime.
use backtrace::Backtrace;
fn caller_name_slow() -> Option<String> {
let backtrace = Backtrace::new();
let symbolname = backtrace.frames().get(2)?.symbols().first()?.name();
symbolname.map(|s| format!("{:#?}", s))
}
fn caller_name_fast() -> Option<String> {
let mut count = 0;
let mut result = None;
backtrace::trace({
|frame| {
count += 1;
if count == 5 {
// Resolve this instruction pointer to a symbol name
backtrace::resolve_frame(frame, |symbol| {
if let Some(name) = symbol.name() {
result = Some(format!("{:#?}", name));
}
});
false
} else {
true // keep going to the next frame
}
}
});
result
}
fn my_function() {
println!("I got called by '{}'.", caller_name_slow().unwrap());
println!("I got called by '{}'.", caller_name_fast().unwrap());
}
fn main() {
my_function();
}
I got called by 'rust_tmp::main'.
I got called by 'rust_tmp::main'.
Note, however, that his is unreliable. The amount of stack frames we have to go up differs between targets and release/debug (due to inlining). For example, on my machine, in release I had to modify count == 5 to count == 2.
Related
In rust, the following function is legal:
fn unwrap<T>(s:Option<T>) -> T {
s.unwrap()
}
It takes ownership of s, panics if it is a None, and returns ownership of the contents of s (which is legal since an Option owns its contents).
I was trying to write a similar function with signature
fn unwrap_set<T>(s: BTreeSet<T>) -> T {
...
}
The idea is that it panics unless s has size 1, in which case it returns the single element. It seems like this should be possible for the same reason unwrap is possible, however none of the methods on BTreeSet had the right signature (they would need to have return type T). The closest was take, and I tried to do
let mut s2 = s;
let v: &T = s2.iter().next().unwrap();
s2.take(v).unwrap()
but this failed.
Is writing a function like unwrap_set possible?
The easiest way to do this would be to use BTreeSet<T>'s implementation of IntoIterator, which would allow you to easily pull owned values out of the set one at a time:
fn unwrap_set<T>(s: BTreeSet<T>) -> T {
let mut it = s.into_iter();
if let Some(first) = it.next() {
if let None = it.next() {
return first;
}
}
panic!("set must have a single value");
}
If you wanted to indirectly rely on IntoIterator you could also use a normal loop, but I don't think it's as readable that way so I probably wouldn't do this:
fn unwrap_set<T>(s: BTreeSet<T>) -> T {
let mut result = None;
for item in s {
// If there is a second value, bail out
if let Some(_) = result {
result = None;
break;
}
result = Some(item);
}
return result.expect("set must have a single value");
}
Edit 1:
When I had written this post I shortened the original code for better presentation. This however also inadvertently introduced a code error in the external library call foo.
I edited the code to fix the rather obvious mistake regarding the handover of a Vec<T> as the community has already correctly pointed out.
Question
How and why does a println or dbg line of code change my runtime behaviour as illustrated? And of more importance: How can I fix my code, such that it works?
I am sincerely sorry for the long post, but I could not leave out any information as it may play a vital role in finding a solution.
Background information
Rust is running as a dynamic system library ([crate_type = "cdylib"]) within a Delphi program. The Delphi program runs within a single process, no other threads are spawned. This process calls Rust, handing over a data object holding a triangle mesh as well as some other parameters. When the line described below is processed, Delphi throws an Invalid_Floating_Point exception.
I have already tested that the handover is interpreted correctly, i.e. I am able to interpret the mesh object as a mesh as well as other parameters handed over.
Note that, if I run the code with a rust example with excately the same setup, the runtime error does not occur.
Rust Code
Call Stack
FFI function:
#[no_mangle]
pub unsafe extern "C" fn foo(
target: &mut *mut Supports,
mesh: *const Mesh
space: f32) -> i32
{
mesh = unsafe { &*mesh };
let rays: Vec<Ray> = generate_rays_from_mesh(&mesh, space);
if let Some(supports) = generate_target(&mesh, rays) {
*target = Box::into_raw(supports);
0
} else {
1
}
}
The mesh stores a BVH for faster processing. generate_target calls the BVH's get_items function. The function intends to find all triangles, which potentially intersect with the ray:
pub fn generate_target(mesh: &Mesh, rays: Vec<Ray>) -> Supports {
...
for ray in rays{
let triangles = mesh.bvh.get_items(ray);
}
}
The BVH tree redirects the get_items call to the root node of the BVH. As the root node is of type Node::Internal, Node being an enum with variants (Internal, Leaf), we dive into the implementation of struct Node. Note that get_items calls itself recursively until we find a leaf node. As the error occurs yet within the first call of Node get_items I omit the code for the leaf.
impl Node {
fn get_items(&self, ray: &Ray) -> Vec<u32> {
match self {
Node::Internal(internal) => {
if intersects_aabb(&ray, &internal.aabb) {
let (mut a, b) = rayon::join(
|| internal.lo.get_items(obj),
|| internal.hi.get_items(obj),
);
a.extend(b.into_iter());
a
} else {
Vec::new()
}
}
Node::Leaf(leaf) => {
...
}
}
}
}
As you can see, the line with the if-statement gets called before the function calls itself recursively. Within this if-statement we dive deeper into the calling stack, as the error occurs here:
#[inline]
pub fn intersects_aabb(ray: &Ray, aabb: &AABB3<f32>) -> Option<f32> {
let mut hit_val = 0.;
let mut max_val = None;
for i in 0..3 {
let s = ray.pos[i];
let d = ray.dir[i];
let min = aabb.min[i];
let max = aabb.max[i];
if relative_eq!(d, 0.0) {
if s < min || max < s {
return None;
}
} else {
let mut t2 = (max - s) / d;
let mut t1 = (min - s) / d;
if t1 > t2 {
mem::swap(&mut t1, &mut t2);
}
hit_val = hit_val.max(t1);
if let Some(m) = max_val {
max_val = ::nalgebra::partial_min(&m, &t2).cloned();
} else {
max_val = Some(t2);
}
if let Some(m) = max_val {
if hit_val > m {
return None;
}
}
}
}
Some(hit_val)
}
Error-throwing part
While debugging, I cannot go beyond the first line of the code shown below.
However, if I debug the values needed for the calculation, all values seem fine, d is not zero as would have already been catched by the if block above these lines.
...
let mut t2 = (max - s) / d;
let mut t1 = (min - s) / d;
...
Changing the function and its result
When I change the function as follows,
let mut t1 = (min - s) / d;
dbg!(&t1);
let mut t1 = (max - s) / d;
dbg!(&t2);
the DLL call does not produce a runtime error.
I need to iterate through and compare a window of unknown length of a string. My current implementation works, however I've done performance tests against it, and it is very inefficient. The method needs to be guaranteed to be safe against Unicode.
fn foo(line: &str, patt: &str) {
for window in line.chars().collect::<Vec<char>>().windows(patt.len()) {
let mut bar = String::new();
for ch in window {
bar.push(*ch);
}
// perform various comparison checks
}
}
An improvement on Shepmaster's final solution, which significantly lowers overhead (by a factor of ~1.5), is
fn foo(line: &str, pattern: &str) -> bool {
let pattern_len = pattern.chars().count();
let starts = line.char_indices().map(|(i, _)| i);
let mut ends = line.char_indices().map(|(i, _)| i);
// Itertools::dropping
if pattern_len != 0 { ends.nth(pattern_len - 1); }
for (start, end) in starts.zip(ends.chain(Some(line.len()))) {
let bar = &line[start..end];
if bar == pattern { return true }
}
false
}
That said, your code from the Github page is a little odd. For instance, you try to deal with different length open and close tags with a wordier version of
let length = cmp::max(comment.len(), comment_end.len());
but your check
if window.contains(comment)
could then trigger multiple times!
Much better would be to just iterate over shrinking slices. In the mini example this would be
fn foo(line: &str, pattern: &str) -> bool {
let mut chars = line.chars();
loop {
let bar = chars.as_str();
if bar.starts_with(pattern) { return true }
if chars.next().is_none() { break }
}
false
}
(Note that this once again ends up again improving performance by another factor of ~1.5.)
and in a larger example this would be something like
let mut is_in_comments = 0u64;
let start = match line.find(comment) {
Some(start) => start,
None => return false,
};
let end = match line.rfind(comment_end) {
Some(end) => end,
None => return true,
};
let mut chars = line[start..end + comment_end.len()].chars();
loop {
let window = chars.as_str();
if window.starts_with(comment) {
if nested {
is_in_comments += 1;
} else {
is_in_comments = 1;
}
} else if window.starts_with(comment_end) {
is_in_comments = is_in_comments.saturating_sub(1);
}
if chars.next().is_none() { break }
}
Note that this still counts overlaps, so /*/ might count as an opening /* immediately followed by a closing */.
The method needs to be guaranteed to be safe against Unicode.
pattern.len() returns the number of bytes that the string requires, so it's already possible that your code is doing the wrong thing. I might suggest you check out tools like QuickCheck to produce arbitrary strings that include Unicode.
Here's my test harness:
use std::iter;
fn main() {
let mut haystack: String = iter::repeat('a').take(1024*1024*100).collect();
haystack.push('b');
println!("{}", haystack.len());
}
And I'm compiling and timing via cargo build --release && time ./target/release/x. Creating the string by itself takes 0.274s.
I used this version of your original code just to have some kind of comparison:
fn foo(line: &str, pattern: &str) -> bool {
for window in line.chars().collect::<Vec<char>>().windows(pattern.len()) {
let mut bar = String::new();
for ch in window {
bar.push(*ch);
}
if bar == pattern { return true }
}
false
}
This takes 4.565s, or 4.291s for just foo.
The first thing I see is that there is a lot of allocation happening on the inner loop. The code creates, allocates, and destroys the String for each iteration. Let's reuse the String allocation:
fn foo_mem(line: &str, pattern: &str) -> bool {
let mut bar = String::new();
for window in line.chars().collect::<Vec<char>>().windows(pattern.len()) {
bar.clear();
bar.extend(window.iter().cloned());
if bar == pattern { return true }
}
false
}
This takes 2.155s or 1.881s for just foo_mem.
Continuing on, another extraneous allocation is the one for the String at all. We already have bytes that look like the right thing, so let's reuse them:
fn foo_no_string(line: &str, pattern: &str) -> bool {
let indices: Vec<_> = line.char_indices().map(|(i, _c)| i).collect();
let l = pattern.chars().count();
for window in indices.windows(l + 1) {
let first_idx = *window.first().unwrap();
let last_idx = *window.last().unwrap();
let bar = &line[first_idx..last_idx];
if bar == pattern { return true }
}
// Do the last pair
{
let last_idx = indices[indices.len() - l];
let bar = &line[last_idx..];
if bar == pattern { return true }
}
false
}
This code is ugly and unidiomatic. I'm pretty sure some thinking (that I'm currently too lazy to do) would make it look a lot better.
This takes 1.409s or 1.135s for just foo_mem.
As this is ~25% of the original time, Amdahl's Law suggests this is a reasonable stopping point.
I have a set of jobs that I am trying to run in parallel. I want to run each task on its own thread and gather the responses on the calling thread.
Some jobs may take much longer than others, so I'd like to start using each result as it comes in, and not have to wait for all jobs to complete.
Here is an attempt:
struct Container<T> {
items : Vec<T>
}
#[derive(Debug)]
struct Item {
x: i32
}
impl Item {
fn foo (&mut self) {
self.x += 1; //consider an expensive mutating computation
}
}
fn main() {
use std;
use std::sync::{Mutex, Arc};
use std::collections::RingBuf;
//set up a container with 2 items
let mut item1 = Item { x: 0};
let mut item2 = Item { x: 1};
let container = Container { items: vec![item1, item2]};
//set a gather system for our results
let ringBuf = Arc::new(Mutex::new(RingBuf::<Item>::new()));
//farm out each job to its own thread...
for item in container.items {
std::thread::Thread::spawn(|| {
item.foo(); //job
ringBuf.lock().unwrap().push_back(item); //push item back to caller
});
}
loop {
let rb = ringBuf.lock().unwrap();
if rb.len() > 0 { //gather results as soon as they are available
println!("{:?}",rb[0]);
rb.pop_front();
}
}
}
For starters, this does not compile due to the impenetrable cannot infer an appropriate lifetime due to conflicting requirements error.
What am I doing wrong and how do I do it right?
You've got a couple compounding issues, but the first one is a misuse / misunderstanding of Arc. You need to give each thread it's own copy of the Arc. Arc itself will make sure that changes are synchronized. The main changes were the addition of .clone() and the move keyword:
for item in container.items {
let mrb = ringBuf.clone();
std::thread::Thread::spawn(move || {
item.foo(); //job
mrb.lock().unwrap().push_back(item); //push item back to caller
});
}
After changing this, you'll run into some simpler errors about forgotten mut qualifiers, and then you hit another problem - you are trying to send mutable references across threads. Your for loop will need to return &mut Item to call foo, but this doesn't match your Vec. Changing it, we can get to something that compiles:
for mut item in container.items.into_iter() {
let mrb = ringBuf.clone();
std::thread::Thread::spawn(move || {
item.foo(); //job
mrb.lock().unwrap().push_back(item); //push item back to caller
});
}
Here, we consume the input vector, moving each of the Items to the worker thread. Unfortunately, this hits the Playpen timeout, so there's probably some deeper issue.
All that being said, I'd highly recommend using channels:
#![feature(std_misc)]
use std::sync::mpsc::channel;
#[derive(Debug)]
struct Item {
x: i32
}
impl Item {
fn foo(&mut self) { self.x += 1; }
}
fn main() {
let items = vec![Item { x: 0 }, Item { x: 1 }];
let rx = {
let (tx, rx) = channel();
for item in items.into_iter() {
let my_tx = tx.clone();
std::thread::Thread::spawn(move || {
let mut item = item;
item.foo();
my_tx.send(item).unwrap();
});
}
rx
};
for item in rx.iter() {
println!("{:?}", item);
}
}
This also times-out in the playpen, but works fine when compiled and run locally.
I've been tinkering with Rust and I'm a little confused with function return types. As an experiment I'm writing an IRC log parser. I'm familiar with the primitive types, and having functions return those. What about more complex types when returning multiple pieces of data?
/* Log line example from log.txt */
/* [17:35] <#botname> name1 [460/702] has challenged name2 [224/739] and taken them in combat! */
#[derive(Show)]
struct Challenger {
challenger: String,
defender: String
}
fn main() {
let path = Path::new("log.txt");
let mut file = BufferedReader::new(File::open(&path));
for line in file.lines() {
let mut unwrapped_line = line.unwrap();
let mut chal = challenges3(unwrapped_line);
println!("Challenger: {}", chal.challenger);
println!("Defender: {}", chal.defender);
}
}
fn challenges3(text: String)-> Challenger {
let s: String = text;
let split: Vec<&str> = s.as_slice().split(' ').collect();
if(split[4] == "has" && split[5] == "challenged") {
let mychallenger = Challenger { challenger: split[2].to_string(), defender: split[6].to_string()};
return mychallenger;
}
}
I realize this code isn't very idiomatic, I'm getting familiar with the language.
I get an error with this code:
"mismatched types: expected `Challenger`, found `()` (expected struct Challenger, found ())"
How can I return a Struct or a HashMap? Is there a better way to return multiple fields of data?
The if in challenges3 has no else block, so if the condition isn't met, execution continues after the if block. There's nothing there, so the function implicitly returns () at this point. You must also return a Challenger after the if block, or panic! to abort the program.
Alternatively, you could change the return type of your function to Option<Challenger>. Return Some(mychallenger) in the if block, and None after the if block:
fn challenges3(text: String) -> Option<Challenger> {
let s: String = text;
let split: Vec<&str> = s.as_slice().split(' ').collect();
if split[4] == "has" && split[5] == "challenged" {
let mychallenger = Challenger { challenger: split[2].to_string(), defender: split[6].to_string()};
return Some(mychallenger);
}
None
}
You can also use Result instead of Option if you want to return some information about the error.