Why does this zig program fail to compile due to "expected error union type, found 'error:124:18'"? - zig

test "error union if" {
var ent_num: error{UnknownEntity}!u32 = error.UnknownEntity;
if (ent_num) |entity| {
try expect(#TypeOf(entity) == u32);
try expect(entity == 5);
} else |err| {
_ = err catch |err1| { // compiles fine when this block is removed
std.debug.print("{s}", .{err1});
};
std.debug.print("{s}", .{err});
}
}
./main.zig:125:5: error: expected error union type, found 'error:124:18'
if (ent_num) |entity| {
^
./main.zig:129:17: note: referenced here
_ = err catch |err1| {

error:124:18 refers to the error{UnknownEntity} because it's
anonymous. so prints the definition address.
the if (my_var) |v| ... syntax can only be used for optional
types. for Error unions you must use try or catch.
try and catch can't be used for Error Set
your code would be this:
const std = #import("std");
const expect = std.testing.expect;
test "error union if" {
var ent_num: error{UnknownEntity}!u32 = error.UnknownEntity;
const entity: u32 = ent_num catch |err| {
std.debug.print("{s}", .{err});
return;
};
try expect(#TypeOf(entity) == u32);
try expect(entity == 5);
}
reading the programs or libraries is very useful for learning a new language. gl

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.

Dynamically parse yaml field to one of a finite set of structs in Go

I have a yaml file, where one field could be represented by one of possible kinds of structs. To simplify the code and yaml files, let's say I have these yaml files:
kind: "foo"
spec:
fooVal: 4
kind: "bar"
spec:
barVal: 5
And these structs for parsing:
type Spec struct {
Kind string `yaml:"kind"`
Spec interface{} `yaml:"spec"`
}
type Foo struct {
FooVal int `yaml:"fooVal"`
}
type Bar struct {
BarVal int `yaml:"barVal"`
}
I know that I can use map[string]interface{} as a type of Spec field. But the real example is more complex, and involves more possible struct types, not only Foo and Bar, this is why I don't like to parse spec into the field.
I've found a workaround for this: unmarshal the yaml into intermediate struct, then check kind field, and marshal map[string]interface{} field into yaml back, and unmarshal it into concrete type:
var spec Spec
if err := yaml.Unmarshal([]byte(src), &spec); err != nil {
panic(err)
}
tmp, _ := yaml.Marshal(spec.Spec)
if spec.Kind == "foo" {
var foo Foo
yaml.Unmarshal(tmp, &foo)
fmt.Printf("foo value is %d\n", foo.FooVal)
}
if spec.Kind == "bar" {
tmp, _ := yaml.Marshal(spec.Spec)
var bar Bar
yaml.Unmarshal(tmp, &bar)
fmt.Printf("bar value is %d\n", bar.BarVal)
}
But it requires additional step and consumes more memory (real yaml file could be bigger than in examples). Does some more elegant way exist to unmarshal yaml dynamically into a finite set of structs?
Update: I'm using github.com/go-yaml/yaml v2.1.0 Yaml parser.
For use with yaml.v2 you can do the following:
type yamlNode struct {
unmarshal func(interface{}) error
}
func (n *yamlNode) UnmarshalYAML(unmarshal func(interface{}) error) error {
n.unmarshal = unmarshal
return nil
}
type Spec struct {
Kind string `yaml:"kind"`
Spec interface{} `yaml:"-"`
}
func (s *Spec) UnmarshalYAML(unmarshal func(interface{}) error) error {
type S Spec
type T struct {
S `yaml:",inline"`
Spec yamlNode `yaml:"spec"`
}
obj := &T{}
if err := unmarshal(obj); err != nil {
return err
}
*s = Spec(obj.S)
switch s.Kind {
case "foo":
s.Spec = new(Foo)
case "bar":
s.Spec = new(Bar)
default:
panic("kind unknown")
}
return obj.Spec.unmarshal(s.Spec)
}
https://play.golang.org/p/Ov0cOaedb-x
For use with yaml.v3 you can do the following:
type Spec struct {
Kind string `yaml:"kind"`
Spec interface{} `yaml:"-"`
}
func (s *Spec) UnmarshalYAML(n *yaml.Node) error {
type S Spec
type T struct {
*S `yaml:",inline"`
Spec yaml.Node `yaml:"spec"`
}
obj := &T{S: (*S)(s)}
if err := n.Decode(obj); err != nil {
return err
}
switch s.Kind {
case "foo":
s.Spec = new(Foo)
case "bar":
s.Spec = new(Bar)
default:
panic("kind unknown")
}
return obj.Spec.Decode(s.Spec)
}
https://play.golang.org/p/ryEuHyU-M2Z
You can do this by implementing a custom UnmarshalYAML func. However, with the v2 version of the API, you would basically do the same thing as you do now and just encapsulate it a bit better.
If you switch to using the v3 API however, you get a better UnmarshalYAML that actually lets you work on the parsed YAML node before it is processed into a native Go type. Here's how that looks:
package main
import (
"errors"
"fmt"
"gopkg.in/yaml.v3"
)
type Spec struct {
Kind string `yaml:"kind"`
Spec interface{} `yaml:"spec"`
}
type Foo struct {
FooVal int `yaml:"fooVal"`
}
type Bar struct {
BarVal int `yaml:"barVal"`
}
func (s *Spec) UnmarshalYAML(value *yaml.Node) error {
s.Kind = ""
for i := 0; i < len(value.Content)/2; i += 2 {
if value.Content[i].Kind == yaml.ScalarNode &&
value.Content[i].Value == "kind" {
if value.Content[i+1].Kind != yaml.ScalarNode {
return errors.New("kind is not a scalar")
}
s.Kind = value.Content[i+1].Value
break
}
}
if s.Kind == "" {
return errors.New("missing field `kind`")
}
switch s.Kind {
case "foo":
var foo Foo
if err := value.Decode(&foo); err != nil {
return err
}
s.Spec = foo
case "bar":
var bar Bar
if err := value.Decode(&bar); err != nil {
return err
}
s.Spec = bar
default:
return errors.New("unknown kind: " + s.Kind)
}
return nil
}
var input1 = []byte(`
kind: "foo"
spec:
fooVal: 4
`)
var input2 = []byte(`
kind: "bar"
spec:
barVal: 5
`)
func main() {
var s1, s2 Spec
if err := yaml.Unmarshal(input1, &s1); err != nil {
panic(err)
}
fmt.Printf("Type of spec from input1: %T\n", s1.Spec)
if err := yaml.Unmarshal(input2, &s2); err != nil {
panic(err)
}
fmt.Printf("Type of spec from input2: %T\n", s2.Spec)
}
I suggest looking into the possibility of using YAML tags instead of your current structure to model this in your YAML; tags have been designed exactly for this purpose. Instead of the current YAML
kind: "foo"
spec:
fooVal: 4
you could write
--- !foo
fooVal: 4
Now you don't need the describing structure with kind and spec anymore. Loading this would look a bit different as you'd need a wrapping root type you can define UnmarshalYAML on, but it may be feasible if this is just a part of a larger structure. You can access the tag !foo in the yaml.Node's Tag field.

Vala get file modification date

I'm new to Vala and linux programming in general.
Im am trying to enumerate the data similar to the 'stat' shell utility for a given folder.
So far it's this i got:
int main (string[] args) {
try {
File directory = File.new_for_path (".");
if (args.length > 1) {
directory = File.new_for_commandline_arg (args[1]);
}
FileEnumerator enumerator = directory.enumerate_children (FileAttribute.TIME_MODIFIED, 0);
FileInfo file_info;
while ((file_info = enumerator.next_file ()) != null) {
DateTime t = file_info.get_modification_date_time();
}
} catch (Error e) {
stderr.printf ("Error: %s\n", e.message);
return 1;
}
return 0;
}
Console output:
vala --pkg gio-2.0 --pkg glib-2.0 main3.vala
main3.vala:16.24-16.59: error: The name `get_modification_date_time' does not exist in the context of `GLib.FileInfo?'
Could someone point me in the right direction?
Thanks.
The error is saying the method doesn't exist. Looking at Valadoc.org for get_modification_date_time it shows this was introduced in GLib version 2.62. That version was released 05 September 2019. It is likely your distribution doesn't include that release yet.
You can either try to update your version of GLib or use the now deprecated get_modification_time:
int main(string[] args) {
if (args[1] == null) {
stderr.printf("No filename given\n");
return 1;
}
var file = GLib.File.new_for_path (args[1]);
try {
GLib.FileInfo info = file.query_info("*", FileQueryInfoFlags.NONE);
print (info.get_modification_time().to_iso8601() + "\n");
print ("\n\nFull info:\n");
foreach (var item in info.list_attributes (null)) {
print( #"$item - $(info.get_attribute_as_string (item))\n" );
}
} catch (Error error) {
stderr.printf (#"$(error.message)\n");
return 1;
}
return 0;
}

Why does the error method return an error?

I want to validate input corresponding to the following grammar snippet:
Declaration:
name = ID "=" brCon=BracketContent
;
BracketContent:
decCon=DecContent (comp+=COMPARATOR content+=DecContent)*
;
DecContent:
(neg=("!"|"not"))? singleContent=VarContent (op+=OPERATOR nextCon+=VarContent)*
;
My validation looks like that:
#Check
def checkNoCycleInHierarchy(Declaration dec) {
if(dec.decCon.singleContent.reference == null) {
return
}
var names = newArrayList
var con = dec.decCon.singleContent
while(con.reference != null) {
con = getThatReference(con).singleContent
if(names.contains(getParentName(con))) {
val errorMsg = "Cycle in hierarchy!"
error(errorMsg,
SQFPackage.eINSTANCE.bracketContent_DecCon,
CYCLE_IN_HIERARCHY)
return
}
names.add(getParentName(con))
}
}
But when I test this validation with a testCaseit returns me an error message:
Expected ERROR 'raven.sqf.CycleInHierarchy' on Declaration at [-1:-1] but got
ERROR (org.eclipse.emf.ecore.impl.EClassImpl#5a7fe64f (name: Declaration) (instanceClassName: null) (abstract: false, interface: false).0) 'Error executing EValidator', offset null, length null
ERROR (org.eclipse.emf.ecore.impl.EClassImpl#5a7fe64f (name: Declaration) (instanceClassName: null) (abstract: false, interface: false).0) 'Error executing EValidator', offset null, length null
I just can't figure out what's wrong with it so I hope that someone of you might have an idea.
Greetings Krzmbrzl
You test utility tells you that the validator did not produce the expected validation error ("CycleInHierarchy").
Instead, the validator produced the error "Error executing EValidator".
Which means an exception has been thrown when your validator was executed.
It turned out it was an internal error...I'm still not exactly sure what went wrong but I have rewritten my validation method and now it works as expected.
Now the method looks like this:
enter code here#Check
def checkNoCycleInHierarchy(Declaration dec) {
if(dec.varContent.reference == null) {
//proceed only if there is a reference
return
}
var content = dec.varContent
var names = newArrayList
while(content.reference != null && !names.contains(getParentName(content))) {
names.add(getParentName(content))
content = content.reference.varContent
if(names.contains(getParentName(content))) {
val errorMsg = "Cycle in hierarchy!"
error(errorMsg,
SQFPackage.eINSTANCE.declaration_BrCon,
CYCLE_IN_HIERARCHY)
return
}
}
}
I have the suspicion that there was a problem with the usage of my "getThatReference" in this case.
Greeting Krzmbrzl

Unmarshalling XML with (xpath)conditions

I'm trying to unmarshall some XML which is structured like the following example:
<player>
<stat type="first_name">Somebody</stat>
<stat type="last_name">Something</stat>
<stat type="birthday">06-12-1987</stat>
</player>
It's dead easy to unmarshal this into a struct like
type Player struct {
Stats []Stat `xml:"stat"`
}
but I'm looking to find a way to unmarshal it into a struct that's more like
type Player struct {
FirstName string `xml:"stat[#Type='first_name']"`
LastName string `xml:"stat[#Type='last_name']"`
Birthday Time `xml:"stat[#Type='birthday']"`
}
is there any way to do this with the standard encoding/xml package?
If not, can you give me a hint how one would split down such a "problem" in go? (best practices on go software architecture for such a task, basically).
thank you!
The encoding/xml package doesn't implement xpath, but does have a simple set of selection methods it can use.
Here's an example of how you could unmarshal the XML you have using encoding/xml. Because the stats are all of the same type, with the same attributes, the easiest way to decode them will be into a slice of the same type. http://play.golang.org/p/My10GFiWDa
var doc = []byte(`<player>
<stat type="first_name">Somebody</stat>
<stat type="last_name">Something</stat>
<stat type="birthday">06-12-1987</stat>
</player>`)
type Player struct {
XMLName xml.Name `xml:"player"`
Stats []PlayerStat `xml:"stat"`
}
type PlayerStat struct {
Type string `xml:"type,attr"`
Value string `xml:",chardata"`
}
And if it's something you need to transform often, you could do the transformation by using an UnamrshalXML method: http://play.golang.org/p/htoOSa81Cn
type Player struct {
XMLName xml.Name `xml:"player"`
FirstName string
LastName string
Birthday string
}
func (p *Player) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
for {
t, err := d.Token()
if err == io.EOF {
break
} else if err != nil {
return err
}
if se, ok := t.(xml.StartElement); ok {
t, err = d.Token()
if err != nil {
return err
}
var val string
if c, ok := t.(xml.CharData); ok {
val = string(c)
} else {
// not char data, skip for now
continue
}
// assuming we have exactly one Attr
switch se.Attr[0].Value {
case "first_name":
p.FirstName = val
case "last_name":
p.LastName = val
case "birthday":
p.Birthday = val
}
}
}
return nil
}

Resources