using mem.eql - unable to evaluate constant expression - zig

Can someone explain me why this piece of code can't be compiled?
const std = #import("std");
const ParseError = error { NotAValidField };
const TestEnum = enum {
field_1,
field_2,
pub fn fromString(str: []const u8) !TestEnum {
switch(true) {
std.mem.eql(u8, "field_1", str) => TestEnum.field_1,
std.mem.eql(u8, "field_2", str) => TestEnum.field_2,
else => ParseError.NotAValidField,
}
}
};
pub fn main() void {
const field = "field_1";
try TestEnum.fromString(field);
}
It results to an error:
./example.zig:11:40: error: unable to evaluate constant expression
std.mem.eql(u8, "field_1", str) => TestEnum.field_1,
Is the compiler trying to figure the str during the compile time while it is passed as an argument? Here's the code in godbolt: https://zig.godbolt.org/z/reK6xv7h5
P.S. I already know there is a std.meta.stringToEnum function.

The compiler sees str in mem.eql call as a runtime value, and thus the error. To specify that str is never used at run time, add a comptime keyword like so:
pub fn fromString(comptime str: []const u8) TestEnum {
return switch (true) {
std.mem.eql(u8, "field_1", str) => TestEnum.field_1,
std.mem.eql(u8, "field_2", str) => TestEnum.field_2,
};
}
Note that all of this comes from wanting to use the mem.eql results to match true. This limits the number of enum values to exactly two and requires the string to be known at compile time. What meta.stringToEnum does instead is to perform the operation at run time.

Related

Incorrect initial capacity for ArrayList

I've been going through ziglearn and have made my way to ArrayList. I understand the example given there but when I try something a bit more complex I run into errors. Based on the error it seems like my array doesn't have valid memory when it goes to append the new element, but I've set the initial capacity to 10. What am I doing wrong?
const std = #import("std");
const page_allocator = std.heap.page_allocator;
const Allocator = std.mem.Allocator;
const C = struct {
list: std.ArrayList(A),
pub fn init(allocator: Allocator) !*C {
var a = try std.ArrayList(A).initCapacity(allocator, 10);
return &C{ .list = a };
}
pub fn info(self: *C) void {
std.log.info("len {} cap {}", .{ self.list.items.len, self.list.capacity });
}
pub fn addElement(self: *C, a: A) !*C {
try self.list.append(a);
return self;
}
};
const A = struct { e: []const u8 };
test "with arraylist" {
var foo = try C.init(page_allocator);
foo.info();
_ = try foo.addElement(.{ .e = "bar" });
}
Can see below the initial capacity is not 10, it changes with each run pointing to its uninitialized. Am I missing a step to initialize memory for the ArrayList?
[default] (info): len 0 cap 140728248984328
Segmentation fault at address 0x0
/home/john/zig/zig-linux-x86_64-0.10.0/lib/std/mem/Allocator.zig:159:30: 0x219c3c in reallocAdvancedWithRetAddr__anon_4653 (test)
return self.vtable.resize(self.ptr, buf, buf_align, new_len, len_align, ret_addr);
^
/home/john/zig/zig-linux-x86_64-0.10.0/lib/std/mem/Allocator.zig:356:43: 0x216faf in reallocAtLeast__anon_3534 (test)
return self.reallocAdvancedWithRetAddr(old_mem, old_alignment, new_n, .at_least, #returnAddress());
^
/home/john/zig/zig-linux-x86_64-0.10.0/lib/std/array_list.zig:353:89: 0x215697 in ensureTotalCapacityPrecise (test)
const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), new_capacity);
^
/home/john/zig/zig-linux-x86_64-0.10.0/lib/std/array_list.zig:338:55: 0x2170f6 in ensureTotalCapacity (test)
return self.ensureTotalCapacityPrecise(better_capacity);
^
/home/john/zig/zig-linux-x86_64-0.10.0/lib/std/array_list.zig:377:41: 0x21577c in addOne (test)
try self.ensureTotalCapacity(newlen);
^
/home/john/zig/zig-linux-x86_64-0.10.0/lib/std/array_list.zig:167:49: 0x213c16 in append (test)
const new_item_ptr = try self.addOne();
^
src/main.zig:154:29: 0x213b86 in addElement (test)
try self.list.append(a);
^
src/main.zig:165:27: 0x213ce7 in test.with arraylist (test)
_ = try foo.addElement(.{ .e = "bar" });
^
/home/john/zig/zig-linux-x86_64-0.10.0/lib/test_runner.zig:63:28: 0x2164f0 in main (test)
} else test_fn.func();
^
/home/john/zig/zig-linux-x86_64-0.10.0/lib/std/start.zig:596:22: 0x21463b in posixCallMainAndExit (test)
root.main();
^
/home/john/zig/zig-linux-x86_64-0.10.0/lib/std/start.zig:368:5: 0x214101 in _start (test)
#call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
^
Your C.init function returns a pointer to C which is on the stack of the init function. This pointer becomes invalid as soon as the function exits.
Just don't return it as a pointer:
pub fn init(allocator: Allocator) !C {
var a = try std.ArrayList(A).initCapacity(allocator, 10);
return C{ .list = a };
}
Or, alternatively, you could put C into the allocator's memory:
pub fn init(allocator: Allocator) !*C {
var result = try allocator.create(C);
result.list = try std.ArrayList(A).initCapacity(allocator, 10);
return result;
}
But avoid doing this unless you have a good reason, as it limits what you can do with C.

Parsing command line arguments in Rust

I am working on a command line program where I need to parse the cli arguments. My problem is that there is an error when I try to parse elements from a vector of Strings
I have a function called ìnto_num_vec() which takes a vector of Strings and I should parse it into a new vector of integers.
Code from lib.rs
pub fn affirm_args(input: Vec<String>) {
if input.len() < 2 {
panic!("To few arguments");
} else {
let numbers = into_num_vec(input);
print_numbers(numbers);
}
}
fn into_num_vec(input: Vec<String>) -> Vec<i32> {
let mut collection: Vec<i32> = Vec::new();
for i in input {
match i.trim().parse() {
Ok(n) => collection.push(n),
Err(_) => panic!("Error parsing")
}
}
collection
}
pub fn print_numbers(input: Vec<i32>) {
for i in input {
println!("{}", i);
}
}
The function is panicking and I'am getting the custom panic msg "Error parsing".
Code in main.rs
use sort_program::*;
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
affirm_args(args);
}
The first argument to a program traditionally is the executable name. You should skip it:
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
affirm_args(args);
}

Current Way to Get User Input in Zig

I'm following this blog post on 'comptime' in Zig.
The following line no longer compiles in Zig 0.6.0.
const user_input = try io.readLineSlice(buf[0..]);
Below is the full function:
fn ask_user() !i64 {
var buf: [10]u8 = undefined;
std.debug.warn("A number please: ");
const user_input = try io.readLineSlice(buf[0..]);
return fmt.parseInt(i64, user_input, 10);
}
What is the equivalent in the current version (of getting user input)?
You can use the method readUntilDelimiterOrEof of stdin instead:
const stdin = std.io.getStdIn().reader();
pub fn readUntilDelimiterOrEof(self: #TypeOf(stdin), buf: []u8, delimiter: u8) !?[]u8
So, the code can be:
fn ask_user() !i64 {
const stdin = std.io.getStdIn().reader();
const stdout = std.io.getStdOut().writer();
var buf: [10]u8 = undefined;
try stdout.print("A number please: ", .{});
if (try stdin.readUntilDelimiterOrEof(buf[0..], '\n')) |user_input| {
return std.fmt.parseInt(i64, user_input, 10);
} else {
return #as(i64, 0);
}
}
See also: Zig 0.7.0 documentation.

How can I efficiently extract the first element of a futures::Stream in a blocking manner?

I've got the following method:
pub fn load_names(&self, req: &super::MagicQueryType) -> ::grpcio::Result<::grpcio::ClientSStreamReceiver<String>> {
My goal is to get the very first element of grpcio::ClientSStreamReceiver; I don't care about the other names:
let name: String = load_names(query)?.wait().nth(0)?;
It seems inefficient to call wait() before nth(0) as I believe wait() blocks the stream until it receives all the elements.
How can I write a more efficient solution (i.e., nth(0).wait()) without triggering build errors? Rust's build errors for futures::stream::Stream look extremely confusing to me.
The Rust playground doesn't support grpcio = "0.4.4" so I cannot provide a link.
To extract the first element of a futures::Stream in a blocking manner, you should convert the Stream to an iterator by calling executor::block_on_stream and then call Iterator::next.
use futures::{executor, stream, Stream}; // 0.3.4
use std::iter;
fn example() -> impl Stream<Item = i32> {
stream::iter(iter::repeat(42))
}
fn main() {
let v = executor::block_on_stream(example()).next();
println!("{:?}", v);
}
If you are using Tokio, you can convert the Stream into a Future with StreamExt::into_future and annotate a function with #[tokio::main]:
use futures::{stream, Stream, StreamExt}; // 0.3.4
use std::iter;
use tokio; // 0.2.13
fn example() -> impl Stream<Item = i32> {
stream::iter(iter::repeat(42))
}
#[tokio::main]
async fn just_one() -> Option<i32> {
let (i, _stream) = example().into_future().await;
i
}
fn main() {
println!("{:?}", just_one());
}
See also:
How do I synchronously return a value calculated in an asynchronous Future in stable Rust?
How to select between a future and stream in Rust?

Generic fn, channel, and thread spawn

I have this code here: (Playground link)
use std::thread;
use std::sync::mpsc::channel;
fn run<T: Send>(task: fn() -> T) -> T {
let (tx, rx) = channel();
thread::spawn(move || {
tx.send(task());
});
rx.recv().unwrap()
}
fn main() {
let task = || 1 + 2;
let result = run(task);
println!("{}", result);
}
But I'm getting a lifetime error I can't figure out.
<anon>:6:5: 6:18 error: the parameter type `T` may not live long enough [E0310]
<anon>:6 thread::spawn(move || {
^~~~~~~~~~~~~
<anon>:6:5: 6:18 help: consider adding an explicit lifetime bound `T: 'static`...
<anon>:6:5: 6:18 note: ...so that captured variable `tx` does not outlive the enclosing closure
<anon>:6 thread::spawn(move || {
^~~~~~~~~~~~~
<anon>:15:22: 15:26 error: mismatched types:
expected `fn() -> _`,
found `[closure <anon>:13:16: 13:24]`
(expected fn pointer,
found closure) [E0308]
<anon>:15 let result = run(task);
^~~~
Any suggestions? Thanks!
The error message suggests adding a 'static bound to the type parameter T. If you do this, it will get rid of the first error:
fn run<T: Send + 'static>(task: fn() -> T) -> T
The 'static bound is needed to guarantee that the value returned by task can outlive the function where task runs. Read more about the 'static lifetime.
The second error is that you are passing a closure, while run expects a function pointer. One way to fix this is by changing task from a closure to a fn:
fn task() -> u32 { 1 + 2 }
Here's the complete working code:
use std::thread;
use std::sync::mpsc::channel;
fn run<T: Send + 'static>(task: fn() -> T) -> T {
let (tx, rx) = channel();
thread::spawn(move || {
tx.send(task());
});
rx.recv().unwrap()
}
fn main() {
fn task() -> u32 { 1 + 2 }
let result = run(task);
println!("{}", result);
}

Resources