First of all, apologies if this question is confused since I'm just trying out Go and have no idea what I'm doing. I have a struct composed of a variety of attributes of different types, example:
type foo struct {
bar string
baz int
bez []string
(...)
Initially I wanted to iterate over all these attributes and print the value if it existed, but I realized you cannot range over a struct the same way you could, say, a list or map. So I've tried out a few tricks with no luck (like trying to iterate over a separate list of attributes), and I think it's better I just ask for help because I'm probably in over my head here.
The idea is that if I create a new instance of this struct, I'd like to be able to then only print values that are set:
obj := foo{"bar_string", 1}
Given that the string slice bez is not set in obj, I'd like to be able to do something like (pseudo):
for i in obj:
print i
Giving:
"bar_string"
1
Ideally, not printing [] which I guess is the zero value for bez.
Am I approaching this whole thing wrong? The reason I'm not using a map is because I'd like the attributes to be different types, and I'd like future differing objects I'm working in to be organized into structs for clarity.
Go doesn't have builtin struct iteration. The for ... range statement is applicable only to:
all entries of an array, slice, string or map, or values received on a channel
or defined types with one of those underlying types (e.g. type Foo []int)
If you want to iterate over a struct known at compile time, you might be better off just accessing fields one by one.
If you want to must iterate over a struct not known at compile time, you can use the reflect package (not recommended):
type Foo struct {
Bar string
Baz int
Quux []int
}
// x := Foo{"bar", 1, nil}
func printAny(x interface{}) {
v := reflect.ValueOf(x)
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
if !reflect.DeepEqual(field.Interface(), reflect.Zero(field.Type()).Interface()) {
fmt.Println(field)
// bar
// 1
}
}
}
...but it's slower and there are some gotchas, for example:
field.Interface() panics if the field is unexported
in the if clause you can't just use the comparison operator == because operands might be not comparable:
you have to make sure that the zero value for field types is what you expect
If your goal is to just print the struct, you can simply implement the Stringer interface, where you can do type-safe checks the way you want without reflect:
type Foo struct {
Bar string
Baz int
Quux []int
}
func (f Foo) String() string {
s := []string{f.Bar, strconv.Itoa(f.Baz)}
if f.Quux != nil {
s = append(s, fmt.Sprintf("%v", f.Quux))
}
return strings.Join(s, "\n")
}
func main() {
fmt.Println(Foo{"bar", 1, nil})
// bar
// 1
}
A Go playground
Related
I have an array of value pairs I want to modify. I need to add and remove values from this array as well, so I used a list. When I tried to use a list, I encountered an error.
Error CS1612 - Cannot modify the return value of 'List<(int, float)>.this[int]' because it is not a variable
So I decided I would investigate. I tried using an array instead, and it... worked fine? The following code only throws an error on arr1[0].Item1 += 1;.
static void Main()
{
List<(int, float)> arr1 = new List<(int, float)>() { (0, 0) };
(int, float)[] arr2 = new (int, float)[1];
arr1[0].Item1 += 1; // This line
arr2[0].Item1 += 1;
}
Why are tuple arrays mutable, but lists are not? Is this because arrays are simple blocks of data you can modify easily, but lists have a lot of backend behind them that complicates things? Is there a simple way to get around this, or am I going to have to make my own custom class?
Why are tuple arrays mutable, but lists are not?
The list itself is mutable, but not in the way you're doing it. Note that this isn't anything specific to tuples - it's just the case for any mutable struct.
The list indexer getter returns a value (i.e. a copy of the tuple in your case) - so modifying that value wouldn't modify the copy in the list. The compiler is trying to avoid you making a change to a value that's about to be thrown away. Array access doesn't do that - arr2[0] refers to the variable within the array. (An array is effectively a collection of variables.)
If you want to mutate the list, you can have to fetch the tuple, mutate it, then put it back:
var tuple = arr1[0];
tuple.Item1++;
arr1[0] = tuple;
Note that this also explains why you can't use list access expressions as arguments for ref parameters, but you can do the equivalent for arrays:
public void Method(ref int x) => x++;
public void CallMethod()
{
var list = new List<int> { 0 };
var array = new int[] { 0 };
Method(ref list[0]); // Error
Method(ref array[0]); // Valid
}
I am passing in a string array and an empty integer array into a function. The point of the function is to convert each element of the string array to an integer and store that into the integer array. When I print the integer array from within the function itself, everything is fine. However, when I try to print the integer array outside of the function, it prints an empty array.
employeeDataInt is the integer array, and employeeDataString is the string array.
I apologize if this is a dumb question but I am new to go. Thanks
package main
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"strings"
)
func strToInt(employeeDataString []string, emplyoeeDataInt []int) []int {
for _, i := range employeeDataString[2:] {
j, err := strconv.Atoi(i)
if err != nil {
panic(err)
}
employeeDataInt = append(employeeDataInt, j)
fmt.Println(employeeDataInt) //this prints out the appropriate array
}
return employeeDataInt
}
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter file name: ")
fileName, err := reader.ReadString('\n')
if err != nil {
log.Fatalf("failed opening file: %s", err)
}
fileName = strings.TrimSuffix(fileName, "\n")
file, err := os.Open(fileName)
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
var employeeLine []string
for scanner.Scan() {
employeeLine = append(employeeLine, scanner.Text())
}
file.Close()
var employeeDataString = []int{}
for _, employee := range employeeLine {
employeeDataString := strings.Split(employee, " ")
strToInt(employeeDataString, employeeDataInt)
fmt.Println(playerData2) //this is outputting just `[]`
}
}
You aren't taking the value of the array and thus the Slice you passed into the function might or might not be updated correctly.
strToInt(employeeDataString, employeeDataInt)
// should be
employeeDataInt = strToInt(employeeDataString, employeeDataInt)
And while at it, you are never assigning playerData2. So fmt.Println(playerData2) will always be [].
But aside from that there are some subtle issues with your usage of Arrays/Slices here:
First the difference between Slices and Arrays:
Go does not allow you to directly work with Arrays.
Unless they have a fixed length ([3]int{} or []int{1,2,3]) you aren't actually looking at an array but at a Slice ([]int).
The slice is just a pointer to an array (along with it's capacity and some other info) and it essentially allows Go to safely muck around with arrays because you never grow an existing array (the size of an array is fixed at initialization). So you can never append to an array.
What Go does to give you the illusion of appending to an array is having a larger than required underlying array, and the Slice controls the access to that array. So if the underlying array has a capacity of 5 and you already stored 3 items in it you can do 2 append operations without having to allocate a new array and copy the existing array elements to the new memory location.
So when you are passing a []int you are actually passing an array pointer (by value) around.
This leads to the next gotcha in your code: The use of append.
As mentioned above, append takes a Slice, looks at the underlying array and how much space is actually left and then adds to it or allocates a new array. If a new array is allocated append returns a new slice that points to the new array.
So calling:
foo := []{1,2,3}
append(foo, 4)
append(foo, 5)
append(foo, 6)
fmt.Print(foo)
// => might return 1,2,3,4,5
You always have to take the return value of append otherwise you risk still referencing the "old" slice that didn't get the new items appended.
So the correct way to grow a Slice, or work with Slices in general is to keep in mind that: Slices are passed by value, so always keep updating your variables with the return values of Slice modifying functions.
There are a few issues in your code:
You're discarding the return value of strToInt.
You're trying to utilize employeeDataInt in main but it is undefined there (which should be causing a compile error, not a runtime issue).
You're declaring employeeDataString twice, in two different scopes in main (inside and outside the for loop), with two different types ([]string and []int). The outer-scoped variable is unused, so should also be causing a compile error.
You're printing playerData2 which is never defined or used - again, this should be causing a compiler error, not incorrect behavior.
Given there were compile errors in the code, either some crucial code was missing from your post, or you did not notice/mention the compile errors.
The correct code within main would be:
var employeeDataInt []int // Seems like you just have the wrong variable name here
for _, employee := range employeeLine {
employeeDataString := strings.Split(employee, " ")
// You're missing the assignment here
employeeDataInt = strToInt(employeeDataString, employeeDataInt)
fmt.Println(employeeDataInt) // This was referencing the wrong variable
}
How can I access a slice defined inside the struct?
type Car struct {
Year int
Name string
Type []int
}
//Accessing "Type" array field as below causes error: array out of range.
Car.Type[0] = 12
Car.Type[1] = 15
Car.Type[2] = 11
You mistake slice for array. It must be:
type Car struct {
Year int
Name string
Type [3]int // <---
}
See running code
You should read this tour: https://tour.golang.org/moretypes/6
You can't directly access a slice field if it's not been initialised. You're defining a struct to have 3 fields: Year of type int, this is a simple value that is part of the struct. Same goes for Name. The Type field, however, is a slice. A slice is a reference type. That means it's essentially a hidden struct (called the slice header) with underlying pointer to an array that is allocated dynamically for you. This underlying pointer is, at the time you initialise your variable, nil.
type Car struct {
Year int
Name string
Type []int
}
Can be seen as:
type Car struct {
Year int
Name string
Type struct{
type: "int",
array *[]T
}
}
Not exactly, but you get the idea. When you write:
c := Car{}
All you've allocated is the int, string and the slice header. You must, therefore initialise the slice first:
c := Car{
Year: 2018,
Name: "vroom",
Type: []int{
1, 2, 3,
},
}
There are many ways to initialise the slice, of course. You don't have to set the values just yet, but you could, for example, allocate and initialise the memory you need in one go:
c.Type = make([]int, 3) // allocates an initialised 3 elements in the slice to 0
you can also allocate but not initialise the slice by specifying the capacity (this is useful to avoid reallocating and moving the slice around too often):
c.Type = make([]int, 0, 3)
Alternatively, you can have the runtime do it all for you, using append:
c.Type = append(c.Type, 1, 2, 3)
Some examples here
A bit more background. Slices and maps work similarly, broadly speaking. Because they are reference types, that rely on pointers internally, it's possible for functions that have a slice as a return type to return nil for example. This doesn't work for functions returning an int:
func nilSlice() []int {
return nil
}
Because the return type is a slice, what this function will return is, essentially, an empty slice. Accessing it will result in the same error you occurred: index out of range.
Trying to return nil from a function like this won't even compile:
func nilInt() int {
nil
}
The resulting error will say something like "Can't use nil as type int". Treat slices as pointer types: they need to be safely initialised before use. Always check their length, and if you need to optimise, look at how the builtin append function is implemented. It'll just exponentially grow the capacity of the underlying array. Something that you may not always want. It's trivial to optimise this sort of stuff
You are confusing Slices and Array. Slices are like dynamic arrays. The way you have defined the slice, their index is not defined until they are appended. For the above code:
type Car struct {
Type []int
}
var car Car
car.Type = append(car.Type, 12)
car.Type = append(car.Type, 15)
car.Type = append(car.Type, 11)
Also, Car in your case is a type of object not a object itself. I have declared object car of type Car.
I want to pass an array to an object and store a reference to this array. I want to be able to modify this array within this object and make sure that it's modified everywhere else.
Here is what I am trying to accomplish (how the code doesn't work)
class Foo {
var foo : Array<Int>
init(foo: Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
var a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
print(a) // My goal is that it will print 1,2,3,4,5
My findings so far
A) The array (by default) are passed strange way. It's a reference until you modify an array length. As soon as you modify a length it will be copied and modified. As result, if I append or delete anything from it in the object it won't be seen outside
B) I can use inout on a function parameter. This will allow me to modify it within this function. However, as soon as I will try to assign it to some object member I am again struck by A)
C) I can wrap an array in some Container class. This probably is the cleanest way. However, I serialize/deserialize these objects and I would rather not put it in Container (because I will have to work around some things for serialization and deserialization and sending it to the server).
Are there anything else? Am I missing some Swift construct which allows me to do that?
You'll have to use an NSArray or NSMutableArray for this because Swift Arrays are value types so any assignment will make a copy.
You could make use of Swifts (very un-swifty) UnsafeMutablePointer.
Since (from your post) the behaviour references to arrays can't really seem be trusted, instead keep an UnsafeMutablePointer companion to the class inner array foo as well as any "external" arrays that you want to be binded to foo, in the sense that they are both just pointers to same address in memory.
class Foo {
var foo : [Int]
var pInner: UnsafeMutablePointer<Int>
init(foo: [Int]) {
pInner = UnsafeMutablePointer(foo)
self.foo = Array(UnsafeBufferPointer(start: pInner, count: foo.count))
}
func modify(inout pOuter: UnsafeMutablePointer<Int>) {
foo.append(5) // <-- foo gets new memory adress
pInner = UnsafeMutablePointer(foo)
pOuter = pInner
}
}
var a = [1,2,3,4] // first alloc in memory
var pOuter: UnsafeMutablePointer<Int> = UnsafeMutablePointer(a)
var bar = Foo(foo: a) // 'bar.foo' now at same address as 'a'
print(bar.foo) // [1,2,3,4]
bar.modify(&pOuter) // -> [1,2,3,4,5]
a = Array(UnsafeBufferPointer(start: pOuter, count: bar.foo.count))
/* Same pointer adress, OK! */
print(bar.pInner)
print(pOuter)
/* Naturally same value (same address in memory) */
print(bar.foo)
print(a)
Pointers can be dangerous though (hence the fitting type name), and, again, very un-swifty. Anyway...
/* When you're done: clear pointers. Usually when using
pointers like these you should take care to .destroy
and .dealloc, but here your pointers are just companions
to an Array property (which has a pointer an reference
counter itself), and the latter will take care of the
objects in memory when it goes out of scope. */
bar.pInner = nil
pOuter = nil
Now, what happens when either a or foo goes out of scope, will it break the variable that are not out of scope, or does Swift contain some clever reference counting that realises a memory address is still in use? I haven't investigated this, but feel free to indulge yourself in that.
From the Swift Programming Language,
Structures are always copied when they are passed around in your code, and do not use reference counting.
If you examine the contents of the array variable, you will see that indeed the append works:
class Foo {
var foo : Array
init(_ foo: Array) {
self.foo = foo
}
func modify() {
foo.append(5)
}
func printFoo() {
print("self.foo: \(foo)")
}
}
let a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
bar.printFoo()
print("a: \(a)")
produces
self.foo: [1, 2, 3, 4, 5]
a: [1, 2, 3, 4]
You have taken a copy of a, not a reference to a.
a is declared a constant hence cannot be modified. If you are planning to modify the contents of a, declare it as a variable. i.e.,
var a = [1,2,3,4]
I haven't tested this but, as you are using a class to wrap the array, I see no reason why the following would not work.
class Foo {
var foo : Array<Int>
init(foo: inout Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
let a = [1,2,3,4]
let bar = Foo(&a)
bar.modify()
print("a: \(a)") // a: [1,2,3,4,5]
http://play.golang.org/p/jdWZ9boyrh
I am getting this error
prog.go:29: invalid receiver type *[]Sentence ([]Sentence is an unnamed type)
prog.go:30: cannot range over S (type *[]Sentence)
[process exited with non-zero status]
when my function tries to receive structure array.
What does it mean by an unnamed type? Why can't it be named? I can name it outside function and also pass them as arguments with them being named.
It does not work. So I just passed []Sentence as an argument and solve the problem that I need to. But when passing them as arguments, I had to return a new copy.
I still think that it would be nice if I can just let the function receive the struct array and does not have to return anything.
Like below:
func (S *[]Sentence)MarkC() {
for _, elem := range S {
elem.mark = "C"
}
}
var arrayC []Sentence
for i:=0; i<5; i++ {
var new_st Sentence
new_st.index = i
arrayC = append(arrayC, new_st)
}
//MarkC(arrayC)
//fmt.Println(arrayC)
//Expecting [{0 C} {1 C} {2 C} {3 C} {4 C}]
//but not working
It is not working either with []Sentence.
Is there anyway that I can make a function receive Struct array?
I'm still learning Go but it seems that it wants the type named. You know, "array of sentences" - that is really an anonymous type. You just have to name it.
(also, use for or one-variable form of range to avoid copying elements (and discarding your changes))
type Sentence struct {
mark string
index int
}
type SentenceArr []Sentence
func (S SentenceArr)MarkC() {
for i := 0; i < len(S); i++ {
S[i].mark = "S"
}
}