The following code works correctly - output: You chose Test 1
package main
import (
"fmt"
)
type TNameMap map[int]string
var nameMap TNameMap
func init() {
nameMap = make(TNameMap)
nameMap[1] = "You chose Test 1"
nameMap[2] = "You chose Test 2"
nameMap[3] = "You chose Test 3"
}
func main() {
fmt.Println(nameMap[1])
}
If I comment out the first line in init() i.e //nameMap = make(TNameMap) , I get a panic when main() runs, because nameMap was never initialized:
panic: runtime error: assignment to entry in nil map
But - if in init() I write nameMap := make(TNameMap)
instead of nameMap = make(TNameMap) , I get no panic, but also no output - main() simply runs and process terminates.
I understand that if I use the Initialization operator - nameMap := make(TNameMap) - I have declared a new variable nameMap that is scoped only to the init() function and so only the package level variable var nameMap TNameMap is in scope for main(), resulting in no output, because the package level var holds no map data.
But, I am confused: Why don't I get the panic in that situation? If main() is making the call on the package var, it was never initialized - so why no panic?
According to the Go spec:
A nil map is equivalent to an empty map except that no elements may be
added.
This means that you can read from a nil map, but not write. Just like the panic says "assignment to entry in nil map". If you comment out just the line nameMap = make(TNameMap) it will crash because you attempt to write to it in init (which is where the panic happens). If you comment out the entirety of init the Println will not crash because you're permitted to access (read from) a nil map.
Changing the assignment to a declaration is just masking the real issue here, what's happening is it's making all the assignments valid, and then discarding the result. As long as you make the assignments valid (either by removing them or making a temporary variable), then you will observe the same behavior in Println.
The value returned by a nil map is always the zero value of the value type of the map. So a map[T]string returns "", a map[T]int returns 0, and so on. (Of course, if you check with val,ok := nilMap[key] then ok will be false).
Related
I have a kotlin class with a method
loadElements(e: Iterable<Int>) {
}
This then constructs a new copy of that Iterable as an ArrayList<Int> within the object.
It is a requirement that all the elements in that ArrayList<Int> be non-negative. It is considered a breach of contract by the caller if that is not met. I've been led to believe that "breach of contract" is something to be tested by require(), whereas check() is for testing logic internal to that method. Is this correct ?
All the examples I have seen, have the require() as the very first lines of code within the method. Is it, however, acceptable to run require() in a loop, like this ?
public fun loadElements(e: Iterable<Int>) {
elementArray.clear()
e.forEach {
require(it>=0)
elementArray.add(it)
moduleCount += it
}
if (elementCount %2 == 1)
elementArray.add(0)
check(elementCount %2 == 0)
computeInternalSizes()
}
Thing is, this means that part of the object's internals may already be set-up by the time the require() breach is detected: i.e., moduleCount will be wrong and computeInternalSizes() will never get called.
Now, of course I could just use a separate pass, with the first one checking for the require() condition, and then doing all the real work thereafter. This would mean that if the input came in as a Sequence<Int>, it would be forced to be terminal and multi-iterable.
If the require() throws, I would like to assume that the program cannot continue because a design error has occurred somewhere. However, if someone traps the resultant exception, and continues, I will end-up with an incoherent object state.
What is best practice for handling conditions where incoming parameter breaches won't be noticed until some significant unrewindable work has been done ?
I tried using a separate pass for checking for non-negativity. This worked perfectly well but, given that it could be coming from a Sequence or similar, I don't want to have to build the whole sequence, and then traverse that sequence again.
I tried using check(). This works, but it just shows up as an inconsistency in object state, rather than flagging up the incoming parameter validation, which is making a breach of contract look like an internal design fault, and just delaying the inevitable.
I've tried putting try/catch/finally all over the place, but this is an excessive amount of code for such a simple thing.
I'm not even sure if a program should attempt recovery if a require() fails.
In general you avoid situations like this, by reducing the scope of mutability in your code.
The difference between require and check is mostly a convention. They throw different Exceptions, namely IllegalArgumentException and IllegalStateException respectively. As the type of the Exceptions suggest, former is suited for validating the (user) input to a method whereas the latter is designed to check intermediate states during the runtime.
Exceptions in Kotlin should be handled as such, being an Exception that should not occur regularly. See also the Kotlin documentation why there are no checked exceptions in Kotlin.
You did not write the name of your surrounding Kotlin class, thus I'll call it Foo for the time being.
Rather than providing a function on Foo, that mutates the internal state of Foo, you could create new instances of Foo based on the Iterable<Int> / Sequence<Int>. This way, you only ever have an Foo object when its in a valid state.
private class Foo(source: Iterable<Int>) {
private val elementArray = ArrayList<Int>()
private val moduleCount: Int
init {
var internalCount = 0
for (count in source) {
require(count > 0)
elementArray.add(count)
internalCount += count
}
moduleCount = internalCount
if (elementArray.size % 2 == 1) {
elementArray.add(0)
}
check(elementArray.size % 2 == 0)
// ...
}
}
Alternatively, if you want / need to keep the interface as described in your question but also avoid the invalid state, you could make use of an intermediate copy.
As you're copying the incoming Iterable<Int> / Sequence<Int> into an ArrayList<Int> I assume you're not working with very large collections.
private class Foo(source: Iterable<Int>) {
private val elementArray = ArrayList<Int>()
private var moduleCount = 0
public fun loadElements(source: Iterable<Int>) {
val internalCopy = ArrayList<Int>()
for (count in source) {
require(count >= 0)
internalCopy.add(count)
}
elementArray.clear()
for (count in internalCopy) {
elementArray.add(count)
moduleCount += count
}
if (elementArray.size % 2 == 1) {
elementArray.add(0)
}
check(elementArray.size % 2 == 0)
// ...
}
}
The following example of Kotlin source code returns an error when compiled:
fun main() {
var index: Int // create an integer used to call an index of an array
val myArray = Array(5) {i -> i + 1} // create an array to call from
val condition = true // makes an if statement run true later
if (condition) {
index = 2 // sets index to 2
}
println( myArray[index] ) // should print 2; errors
}
The error says that the example did not initialize the variable index by the time it is called, even though it is guaranteed to initialize within the if statement. I understand that this problem is easily solved by initializing index to anything before the if statement, but why does the compiler not initialize it? I also understand that Kotlin is still in beta; is this a bug, or is it intentional? Finally, I am using Replit as an online IDE; is there a chance that the compiler on the website simply is an outdated compiler?
The compiler checks whether there is a path in your code that the index may not be initialized based on all the path available in your code apart from the value of the parameters. You have an if statement without any else. If you add the else statement you will not get any compile error.
class Solution {
val message: String //error : val must be initialized or abstract
message = "love" //error : val cannot be reassigned
}
I understand what's happening in here - val cannot be reassigned.
So when I need val but can not initialize it i used to use by lazy
class Solution {
fun love(){
val message : String
message = "love" //this works
message = "hate" //this is error "val cannot be reassigned"
}
}
Here I can delcare val without initialization and later write codemessage = "love".what's happening here?
#deHaar noticed correctly that only var (mutable variable) is appropriate in your case.
The error you get is absolutely correct and expected.
what's happening here?
When you declare a read-only variable without initializing it you have to make sure that each execution path will have a value in this read-only variable. It means that Kotlin makes sure if your read-only variable was or was not initialized in every place it is used and raises errors if the variable is used inappropriately.
Here you have only one execution path as there are no when or if statements that can split execution into several possible paths.
class Solution {
fun love(){
val message : String
message = "love" // Kotlin knows that `message` was not yet initialized
message = "hate" // Kotlin knows that `message` was yet initialized! It does not allow to modify the value.
}
}
Here is what Kotlin documentation says:
... it is also possible (but discouraged) to split the declaration and the initial assignment, and even to initialize in multiple places based on some condition. You can only read the variable at a point where the compiler can prove that every possible execution path will have initialized it. If you're creating a read-only variable in this way, you must also ensure that every possible execution path assigns to it exactly once.
Example of an execution path
Using when or if statement you create two or more execution paths. Execution paths can be presented as a graph, I'll use #number as a node number. Example:
class Solution {
fun love(){
// #1
val message : String
if (System.currentTimeMillisec() % 2 == 0) {
message = "Not empty"
// #2
}
if (message.isEmpty) { // Error! Message could be not initialized at this point!
println("Empty message")
// #3
}
}
}
Looking at this example, that does not compile, we can calculate at least 3 execution paths.
#1 (none of the if statements was entered. All conditions are false)
#1 -> #2
#1 -> #3
Kotlin can calculate these paths and check if the message variable is initialized in every path it is used. As we can see, as soon as you reach the evaluation of the second if statement (in case of first and third paths) your program will crash because the message has no value. It has no address in memory and a computer which runs this program does not know how to get a value from an address that does not exist.
Now, let's modify this code to make it work:
class Solution {
fun love(){
// #1
val message : String
if (System.currentTimeMillisec() % 2 == 0) {
message = "Not empty"
// #2
} else {
message = ""
// #3
}
if (message.isEmpty) { // Error! Message could be not initialized at this point!
println("Empty message")
// #4
}
}
}
Execution paths:
#1 -> #2
#1 -> #3 -> #4
In this example, Kotlin is sure that the message read-only variable is initialized because there is a 100% chance that one of node 2 or node 3 will be executed. Right after the line where the message gets its initial value (initialized) Kotlin treats this variable as a read-only variable with a value.
Questions are welcome. I will try to simplify this answer.
I have the following function:
override fun countForTicket(dbc: SQLiteDatabase, ticketId: Long): Int {
var ret: Int
dbc.query(
TABLE_SECOND_CHANCE_PRIZES, arrayOf("count(id)"),
"ticket = ?", arrayOf(ticketId.toString()),
null, null, null
).use { c ->
ret = if (c.moveToFirst()) {
c.getInt(0)
} else {
0
}
}
return ret
}
The problem is that in line return ret ret is underlined with red and when trying to compile it gives me error:
Variable 'ret' must be initialized
From my point of view it seems that ret is always initialized. What am I missing?
Is it because the initialization is happening in a lambda and the compiler cannot guarantee that the variable is initialized?
The compiler isn't smart enough to know for sure the lambda will be run once, so it can't figure this out for you.
The reason we don't have this problem with many of the standard library higher-order functions is that they utilize contracts, which tell the compiler what they are doing with the lambda that is passed in (such as guaranteeing that the lambda will be called exactly once).
Unfortunately, Closeable.use() doesn't specify a contract (possibly because of it re-throwing exceptions?).
But use does return the result of calling the lambda, so you could do
val ret = dbc.query(...).use { c ->
if (c.moveToFirst()) {
c.getInt(0)
} else {
0
}
}
The compiler don't allow unsafe variables like that to be returned. A variable must always be something.
In your case, ret is initialized inside a lambda. The compiler doesn't know if this lambda is executed or not. If not, ret remains in its unsafe state. Throwing a NullPointerException at the end.
If you're sure that this variable is always assigned you can look at lateinit variables. You can also put a default value to it var ret = 0 and ommit the else statement.
I'm building a Go library for an API that offers JSON or XML formatted data.
This API requires me to request a session_id every 15 minutes or so, and use that in calls. For example:
foo.com/api/[my-application-id]/getuserprofilejson/[username]/[session-id]
foo.com/api/[my-application-id]/getuserprofilexml/[username]/[session-id]
In my Go library, I'm trying to create a variable outside of the main() func and intend to ping it for a value for every API call. If that value is nil or empty, request a new session id and so on.
package apitest
import (
"fmt"
)
test := "This is a test."
func main() {
fmt.Println(test)
test = "Another value"
fmt.Println(test)
}
What is the idiomatic Go way to declare a globally-accessible variable, but not necesarilly a constant?
My test variable needs to:
Be accessible from anywhere within it's own package.
Be changeable
You need
var test = "This is a test"
:= only works in functions and the lower case 't' is so that it is only visible to the package (unexported).
A more thorough explanation
test1.go
package main
import "fmt"
// the variable takes the type of the initializer
var test = "testing"
// you could do:
// var test string = "testing"
// but that is not idiomatic GO
// Both types of instantiation shown above are supported in
// and outside of functions and function receivers
func main() {
// Inside a function you can declare the type and then assign the value
var newVal string
newVal = "Something Else"
// just infer the type
str := "Type can be inferred"
// To change the value of package level variables
fmt.Println(test)
changeTest(newVal)
fmt.Println(test)
changeTest(str)
fmt.Println(test)
}
test2.go
package main
func changeTest(newTest string) {
test = newTest
}
output
testing
Something Else
Type can be inferred
Alternatively, for more complex package initializations or to set up whatever state is required by the package GO provides an init function.
package main
import (
"fmt"
)
var test map[string]int
func init() {
test = make(map[string]int)
test["foo"] = 0
test["bar"] = 1
}
func main() {
fmt.Println(test) // prints map[foo:0 bar:1]
}
Init will be called before main is run.
If you accidentally use "Func" or "function" or "Function" instead of "func" you will also get:
non-declaration statement outside of function body
Posting this because I initially ended up here on my search to figure out what was wrong.
Short variable declarations i.e. :=, can ONLY be used within functions.
e.g.
func main() {
test := "this is a test"
// or
age := 35
}
Declarations outside a function you must make use of keywords like var, func, const e.t.c depending on what you want (in this case we're using var).
Declaring a variable outside a function makes it accessible within its package.
package apitest
import (
"fmt"
)
// note the value can be changed
var test string = "this is a test"
func main() {
fmt.Println(test)
test = "Another value"
fmt.Println(test)
}
Extra info
If you want the variable to be accessible both within and outside its package, the variable has to be capitalized e.g.
var Test string = "this is a test"
this will make it accessible from any package.
We can declare variables as below:
package main
import (
"fmt"
"time"
)
var test = "testing"
var currtime = "15:04:05"
var date = "02/01/2006"
func main() {
t := time.Now()
date := t.Format("02/01/2006")
currtime := t.Format("15:04:05")
fmt.Println(test) //Output: testing
fmt.Println(currtime)//Output: 16:44:53
fmt.Println(date) // Output: 29/08/2018
}
Outside a function, every statement begins with a keyword (var, func, and so on) and so the := construct is not available.
You can read more information here: https://tour.golang.org/basics/10
I got this error when I was trying to run Go app with function definition like this:
(u *UserService) func GetAllUsers() (string, error) {...} //Error code
The correct way of defining a function (receiver function) was:
func (u *UserService) GetAllUsers() (string, error) {...} //Working code