Rust Ownership and Lifetimes using Combine - rust

I've read the docs on ownership and lifetimes and I think I understand them but I'm having trouble with a specific bit of code.
I have a struct called Branch like this:
struct Branch {
slot: u8,
yaw: u8,
pitch: u8,
length: u8
}
I'm using the combine library (it's a parser combinator) to parse a string into a Branch. The parsers look like this:
let hex_re = Regex:new(r"[0-9a-fA-F]").unwrap();
let hex = || find(&hex_re).map(|s| u8::from_str_radix(s, 16));
let branch = |length: u8| {
(hex(), hex(), hex())
.map( |t| (t.0.unwrap(), t.1.unwrap(), t.2.unwrap()) )
.map( |(slot,yaw,pitch)| Branch { slot, yaw, pitch, length } )
}
The parsers are fairly simple, the first hex takes a regular-expression which matches a single hexidecimal character and maps it into a u8. The second branch maps 3 hex characters into a Branch e.g. 3d2.
The problem arises when I call the parser branch(1).parse("3d2"), the compiler reports an error 'length' does not live long enough. I think I understand this error, if I'm not mistaken it's because length goes out of scope when the closure completes and so the length variable is deallocated even though it is still being used by the newly created Branch.
So, I tried to get around this by converting length: u8 to length: &u8 like so:
let branch = |len: &u8| {
(hex(), hex(), hex())
.map( |t| (t.0.unwrap(), t.1.unwrap(), t.2.unwrap()) )
.map( |(slot,yaw,pitch)| Branch { slot, yaw, pitch, length: *len } )
};
// calling the parser
branch(&(1 as u8)).parse("3d2");
But that results in this error:
type of expression contains references that are not valid during the expression: `combine::combinator::Map<combine::combinator::Map<(combine::combinator::Map<combine::regex::Find<&regex::Regex, &str>, [closure#src\lsystem.rs:26:37: 26:70]>, combine::combinator::Map<combine::regex::Find<&regex::Regex, &str>, [closure#src\lsystem.rs:26:37: 26:70]>, combine::combinator::Map<combine::regex::Find<&regex::Regex, &str>, [closure#src\lsystem.rs:26:37: 26:70]>, combine::combinator::Map<combine::regex::Find<&regex::Regex, &str>, [closure#src\lsystem.rs:26:37: 26:70]>), [closure#src\lsystem.rs:30:19: 30:65]>, [closure#src\lsystem.rs:31:19: 31:80 length:&&u8]>`
I have no idea what this error is about. Any help would be much appreciated.

This solved it:
let hex_re = Regex:new(r"[0-9a-fA-F]").unwrap();
let hex = || find(&hex_re).map(|s| u8::from_str_radix(s, 16));
let branch = |length: u8| {
(hex(), hex(), hex())
.map( |t| (t.0.unwrap(), t.1.unwrap(), t.2.unwrap()) )
.map( move |(slot,yaw,pitch)| Branch { slot, yaw, pitch, length } )
}
placing move on the second .map works.

Related

include_str for null terminated string

I need to read a file into a null terminated string at compile time.
Working in Rust OpenGL. I have a shader source code stored in a separate file. The function that will eventually read the source is gl::ShaderSource from the gl crate. All it needs is a pointer to a null terminated string (the std::ffi::CStr type).
Typically the guides I have seen read the shader source file using include_str!, then at run time allocate a whole new buffer of length +1, then copy the original source into the new buffer and put the terminating 0 at the end. I'd like to avoid all that redundant allocating and copying and just have the correctly null terminated string at compile time.
I realize it is somewhat petty to want to avoid an extra allocation for a short shader file, but the principle could apply to many other types of larger constants.
While scrolling through suggested questions during the preview I saw this: How do I expose a compile time generated static C string through FFI?
which led me to this solution:
let bytes1 = concat!(include_str!("triangle.vertex_shader"), "\0");
let bytes2 = bytes1.as_bytes();
let bytes3 = unsafe {
CStr::from_bytes_with_nul_unchecked(bytes2)
};
println!("{:?}", bytes3);
Does this accomplish avoiding the runtime allocation and copying?
Your code is unsound. It fails to verify there are no interior NUL bytes.
You can use the following function to validate the string (at compile time, with no runtime cost):
pub const fn to_cstr(s: &str) -> &CStr {
let bytes = s.as_bytes();
let mut i = 0;
while i < (bytes.len() - 1) {
assert!(bytes[i] != b'\0', "interior byte cannot be NUL");
i += 1;
}
assert!(bytes[bytes.len() - 1] == b'\0', "last byte must be NUL");
// SAFETY: We verified there are no interior NULs and that the string ends with NUL.
unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }
}
Wrap it in a little nice macro:
macro_rules! include_cstr {
( $path:literal $(,)? ) => {{
// Use a constant to force the verification to run at compile time.
const VALUE: &'static ::core::ffi::CStr = $crate::to_cstr(concat!(include_str!($path), "\0"));
VALUE
}};
}
Then use it:
let bytes = include_cstr!("triangle.vertex_shader");
If there are interior NUL bytes the code will fail to compile.
When CStr::from_bytes_with_nul() becomes const-stable, you will be able to replace to_cstr() with it.
Yes that should work as intended. If you want you can even bundle it into a simple macro.
macro_rules! include_cstr {
($file:expr) => {{
// Create as explicit constant to force from_bytes_with_nul_unchecked to
// perform compile time saftey checks.
const CSTR: &'static ::std::ffi::CStr = unsafe {
let input = concat!($file, "\0");
::std::ffi::CStr::from_bytes_with_nul_unchecked(input.as_bytes())
};
CSTR
}};
}
const VERTEX_SHADER: &'static CStr = include_cstr!("shaders/vert.glsl");
const FRAGMENT_SHADER: &'static CStr = include_cstr!("shaders/frag.glsl");

How to return a single owned record with Diesel?

Actually I'm returing a cloned struct
pub fn matrix_first_or_create(&self, schema: String, symbol: String) -> RewardMatrix {
....
let rewards = reward_matrix
.filter(state.eq(schema.clone()))
.filter(turn_symbol.eq(symbol.clone()))
.limit(1)
.load::<RewardMatrix>(&self.connection)
.expect("Error loading posts");
if rewards.len() > 0 {
return rewards.get(0).unwrap().clone();
}
....
}
Is this the right way to handle a result in Diesel? Can I "extract" and own the first and only result from this query?
What you need here is the .first() method of the RunQueryDsl trait.
Here is an example of how it works:
let reward: RewardMatrix = reward_matrix
.filter(state.eq(schema.clone()))
.filter(turn_symbol.eq(symbol.clone()))
.first(&self.connection)?;
But if your query may not return that row, it’s also good to use the .optional() method of QueryResult:
let reward: Option<RewardMatrix> = reward_matrix
.filter(state.eq(schema.clone()))
.filter(turn_symbol.eq(symbol.clone()))
.first(&self.connection)
.optional()?;
load returns a simple owned Vec<RewardMatrix>, so any solution for getting an owned element from a Vec works here:
if let Some(reward) = rewards.into_iter().next() {
return reward;
}
but Diesel also provides better-suited alternatives like get_result and first that don’t involve Vec at all (combine with optional as recommended in the documentation):
let reward = reward_matrix
.filter(state.eq(&schema))
.filter(turn_symbol.eq(&symbol))
.first::<RewardMatrix>(&self.connection)
.optional()
.expect("Error loading posts");
if let Some(reward) = reward {
return reward;
}

error handling when unwrapping several try_into calls

I have a case where I need to parse some different values out from a vector.
I made a function for it, that returns a option, which either should give a option or a None, depending on whether the unwrapping succeeds.
Currently it looks like this:
fn extract_edhoc_message(msg : Vec<u8>)-> Option<EdhocMessage>{
let mtype = msg[0];
let fcnt = msg[1..3].try_into().unwrap();
let devaddr = msg[3..7].try_into().unwrap();
let msg = msg[7..].try_into().unwrap();
Some(EdhocMessage {
m_type: mtype,
fcntup: fcnt,
devaddr: devaddr,
edhoc_msg: msg,
})
}
But, I would like to be able to return a None, if any of the unwrap calls fail.
I can do that by pattern matching on each of them, and then explicitly return a None, if anything fails, but that would a lot of repeated code.
Is there any way to say something like:
"if any of these unwraps fail, return a None?"
This is exactly what ? does. It's even shorter than the .unwrap() version:
fn extract_error_message(msg: Vec<u8>) -> Option<EdhocMessage> {
let m_type = msg[0];
let fcntup = msg[1..3].try_into().ok()?;
let devaddr = msg[3..7].try_into().ok()?;
let edhoc_msg = msg[7..].try_into().ok()?;
Some(EdhocMessage {
m_type,
fcntup,
devaddr,
edhoc_msg
})
}
See this relevant part of the Rust Book.

How to use mail filter context data?

I am trying to write a mail filter in Rust using the milter crate. I built the example on a Linux VM and it all works fine. However, the example is using u32 as the type of context injected into their handlers, a quite simple example. I instead need to store a string from the handle_header callback through to the handle_eom handler so I can use an incoming header to set the envelope from.
If I log the value of the header in handle_header to console, it writes correctly but by the time it arrives in handle_eom, it has been corrupted/overwritten whatever. I thought that context was supposed to be specifically for this scenario but it seems weird that it uses type inference rather than e.g. a pointer to an object that you can just assign whatever you want to it.
Is my understanding of context wrong or is the code incorrect?
I tried using value and &value in handle_header and it behaves the same way.
use milter::*;
fn main() {
Milter::new("inet:3000#localhost")
.name("BounceRewriteFilter")
.on_header(header_callback)
.on_eom(eom_callback)
.on_abort(abort_callback)
.actions(Actions::ADD_HEADER | Actions::REPLACE_SENDER)
.run()
.expect("milter execution failed");
}
#[on_header(header_callback)]
fn handle_header<'a>(mut context: Context<&'a str>, header: &str, value: &'a str) -> milter::Result<Status> {
if header == "Set-Return-Path" {
match context.data.borrow_mut() {
Some(retpath) => *retpath = &value,
None => {
context.data.replace(value)?;
}
}
}
Ok(Status::Continue)
}
#[on_eom(eom_callback)]
fn handle_eom(mut context: Context<&str>) -> milter::Result<Status> {
match context.data.take() {
Ok(result) => {
println!("Set-return-path header is {}", result.unwrap());
context.api.replace_sender(result.unwrap(), None::<&str>)?;
}
Err(_error) => {}
}
Ok(Status::Continue)
}
Thanks to glts on Github, the author of the crate, the problem was that the string slices passed into the handle_header method were not borrowed by the external code that stores the data pointer so by the time that handle_eom is called, the memory has been reused for something else.
All I had to do was change Context<&str> to Context<String> and convert the strings using mystr.to_owned() and in the reverse direction val = &*mystring

Why is clone required to be called explicitly with Strings in some cases but not others?

I was working in coding dojo trying to learn Rust. In the attached link is all our code and test. However, we got stumped as to why we required calling clone() in one function but not the other.
Why do I need to call game.clone() on line 23 of lib.rs in this link https://cyber-dojo.org/kata/edit/WvEB5z
pub fn say_game_score(game: Game) -> String {
if game.player1.score == game.player2.score {
return say_equal_score(game.player1.score);
}
if can_be_won(game) { // This line required game.clone() WHY???
return say_winning_situation(game); // This line does NOT require game.clone()
}
return format!(
"{} {}",
say_score_name(game.player1.score),
say_score_name(game.player2.score)
);
}
fn say_winning_situation(game: Game) -> String {
if game.player1.score > game.player2.score {
return say_leading_situation(game.player1.name, game.player1.score - game.player2.score);
} else {
return say_leading_situation(game.player2.name, game.player2.score - game.player1.score);
}
}
fn can_be_won(game: Game) -> bool {
return game.player1.score > FORTY || game.player2.score > FORTY;
}
can_be_won(game) causes the variable game to be moved into the function. When you then call say_winning_situation(game) the variable has already moved and cant be used anymore. The Rust compile can actually check these things.
The compiler suggests that you clone the game in the first invocation, so it will be copied instead of moved.
You probably want to use references instead of values in your functions. Only take ownership when you need it. For reading access a reference (which is const by default) is your first choice.
You should read about borrow checking in Rust.

Resources