Beginner solution to basic quiz exercise, a la Gophercises

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
4
down vote

favorite












I just started golang development about two weeks ago and recently finished the recommended introduction book.



I'm now working my way through Gophercises - a sort of collection of exercises to improve beginner's understanding of Golang through small projects.




This is my solution to the first project: writing a quiz (cli-)application.



The requirements are simple:




  1. Read a csv file, each line consisting of a question and an answer:



    5+5,10
    1+1,2
    8+3,11
    1+2,3
    8+6,14
    3+1,4
    1+4,5
    5+1,6
    2+3,5
    3+3,6
    2+4,6
    5+2,7


  2. Print the question to the user


  3. Validate if the supplied answer is correct.


  4. Print the correct answers.



Here is my solution to the problem:



package main

import (
"bufio"
"encoding/csv"
"flag"
"fmt"
"io"
"log"
"os"
)

type q struct
question, answer string


func (q q) ask() bool
fmt.Println(q.question, " equals: ")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
if scanner.Err() != nil
log.Fatal(scanner.Err())

if scanner.Text() == q.answer
return true

return false


func quizLoop(path string, verbose bool)
// Loop should:
// 1. Read records line by line
// 2. Ask the question (i/o)
// 3. Keep score.
file, err := os.Open(path)
correct, lines := 0, 0

if err != nil
log.Fatal(err)

defer file.Close()

reader := csv.NewReader(file)
for
record, err := reader.Read()
if err != nil
if err == io.EOF
break

log.Fatal(err)

q := qquestion: record[0], answer: record[1]
if q.ask()
if verbose
fmt.Println("Correct")

correct++
else if verbose
fmt.Println("Incorrect")

lines++

fmt.Printf("You had %d/%d correct answers!n", correct, lines)


func main()
// Setup flags.
p := flag.String("path", "problems.csv", "Specify the path to the quiz questions.")
v := flag.Bool("verbose", false, "A boolean value to check if you want the program to be verbose or not.")
flag.Parse()

// Invoke loop.
quizLoop(*p, *v)




As mentioned in my introduction I am rather new to the language, and couldn't see any caveats where it could have been beneficial to use things like interfaces or go routines in this particular project.



These are the things I'm the most interested in having reviewed:



  • Best Practices

  • Refactoring

  • How to use more advanced functionality to solve it (ie. go routines or interfaces)

  • Adding unit tests to it. What can be tested?

  • Overall design






share|improve this question



























    up vote
    4
    down vote

    favorite












    I just started golang development about two weeks ago and recently finished the recommended introduction book.



    I'm now working my way through Gophercises - a sort of collection of exercises to improve beginner's understanding of Golang through small projects.




    This is my solution to the first project: writing a quiz (cli-)application.



    The requirements are simple:




    1. Read a csv file, each line consisting of a question and an answer:



      5+5,10
      1+1,2
      8+3,11
      1+2,3
      8+6,14
      3+1,4
      1+4,5
      5+1,6
      2+3,5
      3+3,6
      2+4,6
      5+2,7


    2. Print the question to the user


    3. Validate if the supplied answer is correct.


    4. Print the correct answers.



    Here is my solution to the problem:



    package main

    import (
    "bufio"
    "encoding/csv"
    "flag"
    "fmt"
    "io"
    "log"
    "os"
    )

    type q struct
    question, answer string


    func (q q) ask() bool
    fmt.Println(q.question, " equals: ")
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Scan()
    if scanner.Err() != nil
    log.Fatal(scanner.Err())

    if scanner.Text() == q.answer
    return true

    return false


    func quizLoop(path string, verbose bool)
    // Loop should:
    // 1. Read records line by line
    // 2. Ask the question (i/o)
    // 3. Keep score.
    file, err := os.Open(path)
    correct, lines := 0, 0

    if err != nil
    log.Fatal(err)

    defer file.Close()

    reader := csv.NewReader(file)
    for
    record, err := reader.Read()
    if err != nil
    if err == io.EOF
    break

    log.Fatal(err)

    q := qquestion: record[0], answer: record[1]
    if q.ask()
    if verbose
    fmt.Println("Correct")

    correct++
    else if verbose
    fmt.Println("Incorrect")

    lines++

    fmt.Printf("You had %d/%d correct answers!n", correct, lines)


    func main()
    // Setup flags.
    p := flag.String("path", "problems.csv", "Specify the path to the quiz questions.")
    v := flag.Bool("verbose", false, "A boolean value to check if you want the program to be verbose or not.")
    flag.Parse()

    // Invoke loop.
    quizLoop(*p, *v)




    As mentioned in my introduction I am rather new to the language, and couldn't see any caveats where it could have been beneficial to use things like interfaces or go routines in this particular project.



    These are the things I'm the most interested in having reviewed:



    • Best Practices

    • Refactoring

    • How to use more advanced functionality to solve it (ie. go routines or interfaces)

    • Adding unit tests to it. What can be tested?

    • Overall design






    share|improve this question























      up vote
      4
      down vote

      favorite









      up vote
      4
      down vote

      favorite











      I just started golang development about two weeks ago and recently finished the recommended introduction book.



      I'm now working my way through Gophercises - a sort of collection of exercises to improve beginner's understanding of Golang through small projects.




      This is my solution to the first project: writing a quiz (cli-)application.



      The requirements are simple:




      1. Read a csv file, each line consisting of a question and an answer:



        5+5,10
        1+1,2
        8+3,11
        1+2,3
        8+6,14
        3+1,4
        1+4,5
        5+1,6
        2+3,5
        3+3,6
        2+4,6
        5+2,7


      2. Print the question to the user


      3. Validate if the supplied answer is correct.


      4. Print the correct answers.



      Here is my solution to the problem:



      package main

      import (
      "bufio"
      "encoding/csv"
      "flag"
      "fmt"
      "io"
      "log"
      "os"
      )

      type q struct
      question, answer string


      func (q q) ask() bool
      fmt.Println(q.question, " equals: ")
      scanner := bufio.NewScanner(os.Stdin)
      scanner.Scan()
      if scanner.Err() != nil
      log.Fatal(scanner.Err())

      if scanner.Text() == q.answer
      return true

      return false


      func quizLoop(path string, verbose bool)
      // Loop should:
      // 1. Read records line by line
      // 2. Ask the question (i/o)
      // 3. Keep score.
      file, err := os.Open(path)
      correct, lines := 0, 0

      if err != nil
      log.Fatal(err)

      defer file.Close()

      reader := csv.NewReader(file)
      for
      record, err := reader.Read()
      if err != nil
      if err == io.EOF
      break

      log.Fatal(err)

      q := qquestion: record[0], answer: record[1]
      if q.ask()
      if verbose
      fmt.Println("Correct")

      correct++
      else if verbose
      fmt.Println("Incorrect")

      lines++

      fmt.Printf("You had %d/%d correct answers!n", correct, lines)


      func main()
      // Setup flags.
      p := flag.String("path", "problems.csv", "Specify the path to the quiz questions.")
      v := flag.Bool("verbose", false, "A boolean value to check if you want the program to be verbose or not.")
      flag.Parse()

      // Invoke loop.
      quizLoop(*p, *v)




      As mentioned in my introduction I am rather new to the language, and couldn't see any caveats where it could have been beneficial to use things like interfaces or go routines in this particular project.



      These are the things I'm the most interested in having reviewed:



      • Best Practices

      • Refactoring

      • How to use more advanced functionality to solve it (ie. go routines or interfaces)

      • Adding unit tests to it. What can be tested?

      • Overall design






      share|improve this question













      I just started golang development about two weeks ago and recently finished the recommended introduction book.



      I'm now working my way through Gophercises - a sort of collection of exercises to improve beginner's understanding of Golang through small projects.




      This is my solution to the first project: writing a quiz (cli-)application.



      The requirements are simple:




      1. Read a csv file, each line consisting of a question and an answer:



        5+5,10
        1+1,2
        8+3,11
        1+2,3
        8+6,14
        3+1,4
        1+4,5
        5+1,6
        2+3,5
        3+3,6
        2+4,6
        5+2,7


      2. Print the question to the user


      3. Validate if the supplied answer is correct.


      4. Print the correct answers.



      Here is my solution to the problem:



      package main

      import (
      "bufio"
      "encoding/csv"
      "flag"
      "fmt"
      "io"
      "log"
      "os"
      )

      type q struct
      question, answer string


      func (q q) ask() bool
      fmt.Println(q.question, " equals: ")
      scanner := bufio.NewScanner(os.Stdin)
      scanner.Scan()
      if scanner.Err() != nil
      log.Fatal(scanner.Err())

      if scanner.Text() == q.answer
      return true

      return false


      func quizLoop(path string, verbose bool)
      // Loop should:
      // 1. Read records line by line
      // 2. Ask the question (i/o)
      // 3. Keep score.
      file, err := os.Open(path)
      correct, lines := 0, 0

      if err != nil
      log.Fatal(err)

      defer file.Close()

      reader := csv.NewReader(file)
      for
      record, err := reader.Read()
      if err != nil
      if err == io.EOF
      break

      log.Fatal(err)

      q := qquestion: record[0], answer: record[1]
      if q.ask()
      if verbose
      fmt.Println("Correct")

      correct++
      else if verbose
      fmt.Println("Incorrect")

      lines++

      fmt.Printf("You had %d/%d correct answers!n", correct, lines)


      func main()
      // Setup flags.
      p := flag.String("path", "problems.csv", "Specify the path to the quiz questions.")
      v := flag.Bool("verbose", false, "A boolean value to check if you want the program to be verbose or not.")
      flag.Parse()

      // Invoke loop.
      quizLoop(*p, *v)




      As mentioned in my introduction I am rather new to the language, and couldn't see any caveats where it could have been beneficial to use things like interfaces or go routines in this particular project.



      These are the things I'm the most interested in having reviewed:



      • Best Practices

      • Refactoring

      • How to use more advanced functionality to solve it (ie. go routines or interfaces)

      • Adding unit tests to it. What can be tested?

      • Overall design








      share|improve this question












      share|improve this question




      share|improve this question








      edited Jan 26 at 19:16









      200_success

      123k14143401




      123k14143401









      asked Jan 26 at 17:20









      geostocker

      39618




      39618




















          3 Answers
          3






          active

          oldest

          votes

















          up vote
          2
          down vote













          Two things I noticed:



           file, err := os.Open(path)
          correct, lines := 0, 0

          if err != nil
          log.Fatal(err)

          defer file.Close()



          1. Handle errors immediately, don't do other things first:



            file, err := os.Open(path)
            if err != nil
            log.Fatal(err)

            defer file.Close()

            correct, lines := 0, 0


            Don't leave an opportunity to add more code later before the error handling by separating os.Open from if err != nil. Sooner or later you'll use file where it may be nil.




          2. Creating a new scanner for each answer is slightly wasteful. Since you asked about potential tests, you can clearly test one or two question/answer cycles. To do that, you'll want to pass two io.Readers to quizLoop instead of a filename



            func quizLoop(questions io.Reader, answers io.Reader, verbose bool)


            You can pass the file and os.Stdin from main, and in tests you can pass bytes.Buffers, for instance, to supply test data.



          I don't see an opportunity to leverage Go routines here. This program is inherently sequential. I'm sure you'll get an excercise for that later.






          share|improve this answer

















          • 1




            Even further: func quizLoop(questions io.Reader, answers io.Reader, output io.Writer, log io.Writer) (with ioutil.Discard if no verbosity is wanted)
            – oliverpool
            Feb 2 at 9:20

















          up vote
          0
          down vote













          If you really want to use goroutines and channels, you could read the csv file in a goroutine:



          questions := make(chan q)
          go pushQuestions(questions) // type: func(chan<- questions)
          // it closes the channel when all questions are red

          for q := range questions
          q.ask()
          ...






          share|improve this answer




























            up vote
            0
            down vote













            Design



            The q structure has two roles:



            • Container of a question-answer pair

            • Handle user interaction

            It would be better to separate these responsibilities:
            the structure should not have the ask() function.



            Somewhat related to this,
            there's no good reason to recreate a scanner in the ask function.



            One option could be to create a new ask(...) function that takes as parameters a scanner, a question, and an answer.
            With this approach the q struct becomes pointless.



            To make the q struct legitimate,
            you could write a func qreader(file *os.File, qs chan q) that reads the CSV and pushes q instances to the channel.
            This qreader could run in a goroutine,
            while the main thread reads from the channel and handles user interaction.



            Don't ignore return values



            The program ignores the return value of scanner.Scan().
            It could be useful to save an unnecessary check for scanner.Err().



            It's good to develop the habit to look suspiciously at statements that don't return value (must be mutating state), or non-void statements whose return value is ignored.



            Naming



            Go encourages short names, but I think q is too short and meaningless for a struct. At the minimum qa would have been better, to capture the notion of a question-answer pair. I would call it questionAnswer.






            share|improve this answer





















              Your Answer




              StackExchange.ifUsing("editor", function ()
              return StackExchange.using("mathjaxEditing", function ()
              StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
              StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
              );
              );
              , "mathjax-editing");

              StackExchange.ifUsing("editor", function ()
              StackExchange.using("externalEditor", function ()
              StackExchange.using("snippets", function ()
              StackExchange.snippets.init();
              );
              );
              , "code-snippets");

              StackExchange.ready(function()
              var channelOptions =
              tags: "".split(" "),
              id: "196"
              ;
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function()
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled)
              StackExchange.using("snippets", function()
              createEditor();
              );

              else
              createEditor();

              );

              function createEditor()
              StackExchange.prepareEditor(
              heartbeatType: 'answer',
              convertImagesToLinks: false,
              noModals: false,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: null,
              bindNavPrevention: true,
              postfix: "",
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              );



              );








               

              draft saved


              draft discarded


















              StackExchange.ready(
              function ()
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186067%2fbeginner-solution-to-basic-quiz-exercise-a-la-gophercises%23new-answer', 'question_page');

              );

              Post as a guest






























              3 Answers
              3






              active

              oldest

              votes








              3 Answers
              3






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes








              up vote
              2
              down vote













              Two things I noticed:



               file, err := os.Open(path)
              correct, lines := 0, 0

              if err != nil
              log.Fatal(err)

              defer file.Close()



              1. Handle errors immediately, don't do other things first:



                file, err := os.Open(path)
                if err != nil
                log.Fatal(err)

                defer file.Close()

                correct, lines := 0, 0


                Don't leave an opportunity to add more code later before the error handling by separating os.Open from if err != nil. Sooner or later you'll use file where it may be nil.




              2. Creating a new scanner for each answer is slightly wasteful. Since you asked about potential tests, you can clearly test one or two question/answer cycles. To do that, you'll want to pass two io.Readers to quizLoop instead of a filename



                func quizLoop(questions io.Reader, answers io.Reader, verbose bool)


                You can pass the file and os.Stdin from main, and in tests you can pass bytes.Buffers, for instance, to supply test data.



              I don't see an opportunity to leverage Go routines here. This program is inherently sequential. I'm sure you'll get an excercise for that later.






              share|improve this answer

















              • 1




                Even further: func quizLoop(questions io.Reader, answers io.Reader, output io.Writer, log io.Writer) (with ioutil.Discard if no verbosity is wanted)
                – oliverpool
                Feb 2 at 9:20














              up vote
              2
              down vote













              Two things I noticed:



               file, err := os.Open(path)
              correct, lines := 0, 0

              if err != nil
              log.Fatal(err)

              defer file.Close()



              1. Handle errors immediately, don't do other things first:



                file, err := os.Open(path)
                if err != nil
                log.Fatal(err)

                defer file.Close()

                correct, lines := 0, 0


                Don't leave an opportunity to add more code later before the error handling by separating os.Open from if err != nil. Sooner or later you'll use file where it may be nil.




              2. Creating a new scanner for each answer is slightly wasteful. Since you asked about potential tests, you can clearly test one or two question/answer cycles. To do that, you'll want to pass two io.Readers to quizLoop instead of a filename



                func quizLoop(questions io.Reader, answers io.Reader, verbose bool)


                You can pass the file and os.Stdin from main, and in tests you can pass bytes.Buffers, for instance, to supply test data.



              I don't see an opportunity to leverage Go routines here. This program is inherently sequential. I'm sure you'll get an excercise for that later.






              share|improve this answer

















              • 1




                Even further: func quizLoop(questions io.Reader, answers io.Reader, output io.Writer, log io.Writer) (with ioutil.Discard if no verbosity is wanted)
                – oliverpool
                Feb 2 at 9:20












              up vote
              2
              down vote










              up vote
              2
              down vote









              Two things I noticed:



               file, err := os.Open(path)
              correct, lines := 0, 0

              if err != nil
              log.Fatal(err)

              defer file.Close()



              1. Handle errors immediately, don't do other things first:



                file, err := os.Open(path)
                if err != nil
                log.Fatal(err)

                defer file.Close()

                correct, lines := 0, 0


                Don't leave an opportunity to add more code later before the error handling by separating os.Open from if err != nil. Sooner or later you'll use file where it may be nil.




              2. Creating a new scanner for each answer is slightly wasteful. Since you asked about potential tests, you can clearly test one or two question/answer cycles. To do that, you'll want to pass two io.Readers to quizLoop instead of a filename



                func quizLoop(questions io.Reader, answers io.Reader, verbose bool)


                You can pass the file and os.Stdin from main, and in tests you can pass bytes.Buffers, for instance, to supply test data.



              I don't see an opportunity to leverage Go routines here. This program is inherently sequential. I'm sure you'll get an excercise for that later.






              share|improve this answer













              Two things I noticed:



               file, err := os.Open(path)
              correct, lines := 0, 0

              if err != nil
              log.Fatal(err)

              defer file.Close()



              1. Handle errors immediately, don't do other things first:



                file, err := os.Open(path)
                if err != nil
                log.Fatal(err)

                defer file.Close()

                correct, lines := 0, 0


                Don't leave an opportunity to add more code later before the error handling by separating os.Open from if err != nil. Sooner or later you'll use file where it may be nil.




              2. Creating a new scanner for each answer is slightly wasteful. Since you asked about potential tests, you can clearly test one or two question/answer cycles. To do that, you'll want to pass two io.Readers to quizLoop instead of a filename



                func quizLoop(questions io.Reader, answers io.Reader, verbose bool)


                You can pass the file and os.Stdin from main, and in tests you can pass bytes.Buffers, for instance, to supply test data.



              I don't see an opportunity to leverage Go routines here. This program is inherently sequential. I'm sure you'll get an excercise for that later.







              share|improve this answer













              share|improve this answer



              share|improve this answer











              answered Feb 1 at 17:56









              Peter

              1433




              1433







              • 1




                Even further: func quizLoop(questions io.Reader, answers io.Reader, output io.Writer, log io.Writer) (with ioutil.Discard if no verbosity is wanted)
                – oliverpool
                Feb 2 at 9:20












              • 1




                Even further: func quizLoop(questions io.Reader, answers io.Reader, output io.Writer, log io.Writer) (with ioutil.Discard if no verbosity is wanted)
                – oliverpool
                Feb 2 at 9:20







              1




              1




              Even further: func quizLoop(questions io.Reader, answers io.Reader, output io.Writer, log io.Writer) (with ioutil.Discard if no verbosity is wanted)
              – oliverpool
              Feb 2 at 9:20




              Even further: func quizLoop(questions io.Reader, answers io.Reader, output io.Writer, log io.Writer) (with ioutil.Discard if no verbosity is wanted)
              – oliverpool
              Feb 2 at 9:20












              up vote
              0
              down vote













              If you really want to use goroutines and channels, you could read the csv file in a goroutine:



              questions := make(chan q)
              go pushQuestions(questions) // type: func(chan<- questions)
              // it closes the channel when all questions are red

              for q := range questions
              q.ask()
              ...






              share|improve this answer

























                up vote
                0
                down vote













                If you really want to use goroutines and channels, you could read the csv file in a goroutine:



                questions := make(chan q)
                go pushQuestions(questions) // type: func(chan<- questions)
                // it closes the channel when all questions are red

                for q := range questions
                q.ask()
                ...






                share|improve this answer























                  up vote
                  0
                  down vote










                  up vote
                  0
                  down vote









                  If you really want to use goroutines and channels, you could read the csv file in a goroutine:



                  questions := make(chan q)
                  go pushQuestions(questions) // type: func(chan<- questions)
                  // it closes the channel when all questions are red

                  for q := range questions
                  q.ask()
                  ...






                  share|improve this answer













                  If you really want to use goroutines and channels, you could read the csv file in a goroutine:



                  questions := make(chan q)
                  go pushQuestions(questions) // type: func(chan<- questions)
                  // it closes the channel when all questions are red

                  for q := range questions
                  q.ask()
                  ...







                  share|improve this answer













                  share|improve this answer



                  share|improve this answer











                  answered Feb 2 at 9:25









                  oliverpool

                  1,542425




                  1,542425




















                      up vote
                      0
                      down vote













                      Design



                      The q structure has two roles:



                      • Container of a question-answer pair

                      • Handle user interaction

                      It would be better to separate these responsibilities:
                      the structure should not have the ask() function.



                      Somewhat related to this,
                      there's no good reason to recreate a scanner in the ask function.



                      One option could be to create a new ask(...) function that takes as parameters a scanner, a question, and an answer.
                      With this approach the q struct becomes pointless.



                      To make the q struct legitimate,
                      you could write a func qreader(file *os.File, qs chan q) that reads the CSV and pushes q instances to the channel.
                      This qreader could run in a goroutine,
                      while the main thread reads from the channel and handles user interaction.



                      Don't ignore return values



                      The program ignores the return value of scanner.Scan().
                      It could be useful to save an unnecessary check for scanner.Err().



                      It's good to develop the habit to look suspiciously at statements that don't return value (must be mutating state), or non-void statements whose return value is ignored.



                      Naming



                      Go encourages short names, but I think q is too short and meaningless for a struct. At the minimum qa would have been better, to capture the notion of a question-answer pair. I would call it questionAnswer.






                      share|improve this answer

























                        up vote
                        0
                        down vote













                        Design



                        The q structure has two roles:



                        • Container of a question-answer pair

                        • Handle user interaction

                        It would be better to separate these responsibilities:
                        the structure should not have the ask() function.



                        Somewhat related to this,
                        there's no good reason to recreate a scanner in the ask function.



                        One option could be to create a new ask(...) function that takes as parameters a scanner, a question, and an answer.
                        With this approach the q struct becomes pointless.



                        To make the q struct legitimate,
                        you could write a func qreader(file *os.File, qs chan q) that reads the CSV and pushes q instances to the channel.
                        This qreader could run in a goroutine,
                        while the main thread reads from the channel and handles user interaction.



                        Don't ignore return values



                        The program ignores the return value of scanner.Scan().
                        It could be useful to save an unnecessary check for scanner.Err().



                        It's good to develop the habit to look suspiciously at statements that don't return value (must be mutating state), or non-void statements whose return value is ignored.



                        Naming



                        Go encourages short names, but I think q is too short and meaningless for a struct. At the minimum qa would have been better, to capture the notion of a question-answer pair. I would call it questionAnswer.






                        share|improve this answer























                          up vote
                          0
                          down vote










                          up vote
                          0
                          down vote









                          Design



                          The q structure has two roles:



                          • Container of a question-answer pair

                          • Handle user interaction

                          It would be better to separate these responsibilities:
                          the structure should not have the ask() function.



                          Somewhat related to this,
                          there's no good reason to recreate a scanner in the ask function.



                          One option could be to create a new ask(...) function that takes as parameters a scanner, a question, and an answer.
                          With this approach the q struct becomes pointless.



                          To make the q struct legitimate,
                          you could write a func qreader(file *os.File, qs chan q) that reads the CSV and pushes q instances to the channel.
                          This qreader could run in a goroutine,
                          while the main thread reads from the channel and handles user interaction.



                          Don't ignore return values



                          The program ignores the return value of scanner.Scan().
                          It could be useful to save an unnecessary check for scanner.Err().



                          It's good to develop the habit to look suspiciously at statements that don't return value (must be mutating state), or non-void statements whose return value is ignored.



                          Naming



                          Go encourages short names, but I think q is too short and meaningless for a struct. At the minimum qa would have been better, to capture the notion of a question-answer pair. I would call it questionAnswer.






                          share|improve this answer













                          Design



                          The q structure has two roles:



                          • Container of a question-answer pair

                          • Handle user interaction

                          It would be better to separate these responsibilities:
                          the structure should not have the ask() function.



                          Somewhat related to this,
                          there's no good reason to recreate a scanner in the ask function.



                          One option could be to create a new ask(...) function that takes as parameters a scanner, a question, and an answer.
                          With this approach the q struct becomes pointless.



                          To make the q struct legitimate,
                          you could write a func qreader(file *os.File, qs chan q) that reads the CSV and pushes q instances to the channel.
                          This qreader could run in a goroutine,
                          while the main thread reads from the channel and handles user interaction.



                          Don't ignore return values



                          The program ignores the return value of scanner.Scan().
                          It could be useful to save an unnecessary check for scanner.Err().



                          It's good to develop the habit to look suspiciously at statements that don't return value (must be mutating state), or non-void statements whose return value is ignored.



                          Naming



                          Go encourages short names, but I think q is too short and meaningless for a struct. At the minimum qa would have been better, to capture the notion of a question-answer pair. I would call it questionAnswer.







                          share|improve this answer













                          share|improve this answer



                          share|improve this answer











                          answered Mar 7 at 21:50









                          janos

                          95.6k12120343




                          95.6k12120343






















                               

                              draft saved


                              draft discarded


























                               


                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function ()
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186067%2fbeginner-solution-to-basic-quiz-exercise-a-la-gophercises%23new-answer', 'question_page');

                              );

                              Post as a guest













































































                              Popular posts from this blog

                              Chat program with C++ and SFML

                              Function to Return a JSON Like Objects Using VBA Collections and Arrays

                              Will my employers contract hold up in court?