Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Why do i get the error: "a does not exist in the current context"?

Discussion in 'Scripting' started by Unscriptableeee, Oct 3, 2020.

  1. Unscriptableeee

    Unscriptableeee

    Joined:
    Apr 14, 2020
    Posts:
    12
    I was trying to return the int variable "a" and add one to it but it just gave me that error.

    error on line 9.

    The code:


    [
    using UnityEngine;
    public class script : MonoBehaviour
    {
    private void Start()
    {
    Debug.Log("Called");
    testfunc(5);
    Debug.Log(testfunc(a));
    }
    int testfunc(int a)
    {
    Debug.Log("Thing");
    return a++;
    }
    ]​
     
    Last edited: Oct 3, 2020
  2. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,590
    No 'a' is known to this line. You probably meant to pass a number, like you did with '5' above.

    Also, in the future please use code tags to post code examples and show the full error message, since that would include the line number which saves a lot of time. Have a read: http://plbm.com/?p=220
     
    Unscriptableeee and adamgolden like this.
  3. Unscriptableeee

    Unscriptableeee

    Joined:
    Apr 14, 2020
    Posts:
    12
    Yeah my bad, I'm new to this layout. But if you add a number when printing the value returned wouldn't it be updated again? I'm trying to get the value it returned from the function "a" in this case.
     
  4. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,590
    The function returns a value, but the identifier is only known to that function. So you can do something like
    Code (CSharp):
    1. int b = testfunction(5);
    No 'a' exists in this scope. The variable identifier 'a' only exists within the scope of testfunction.
     
  5. Unscriptableeee

    Unscriptableeee

    Joined:
    Apr 14, 2020
    Posts:
    12
    So it's not possible to pass a value as you do not have access to it? "b" would return 5.
     
  6. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    you either declare a variable in the context of a class (known as a 'field'), or in the context of a function (known as a local variable). additionally, method-body argument identifiers are also considered as locally-accessible variables.

    if some function refers to a variable that isn't a known field and isn't a locally declared variable, nor an argument, you get this error.

    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class script : MonoBehaviour
    4. {
    5.  
    6.   // no 'a' was declared here as a field
    7.  
    8.   private void Start() // no 'a' is declared here either
    9.   {
    10.     Debug.Log("Called");
    11.     testfunc(5);
    12.     Debug.Log(testfunc(a)); // so, where is 'a' in this context?
    13.   }
    14.  
    15.   int testfunc(int a) // a is declared here as an argument
    16.   {
    17.     Debug.Log("Thing");
    18.     return a++; // a is thus recognized locally
    19.   }
    20.  
    21. }
     
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    there are also a couple more places where you can declare local variables, namely in any code block (regardless of whether it was a method body or not), and in loop initializers.

    Code (csharp):
    1. void someMethod()
    2. {
    3.   int a = 7;
    4.   {
    5.     int b = 5;
    6.     Debug.Log(a);
    7.     Debug.Log(b);
    8.   }
    9.   Debug.Log(a);
    10.   Debug.Log(b); // this doesn't work
    11. }
    Code (csharp):
    1. void someMethod()
    2. {
    3.   for(int a = 7, b = 5; a >= 0; a--)
    4.   {
    5.     Debug.Log(a);
    6.     Debug.Log(b);
    7.   }
    8. }
    Code (csharp):
    1. public class someClass
    2. {
    3.   int a = 7;
    4.  
    5.   void someMethod()
    6.   {
    7.     for(int b = 5; b >= 0; b--)
    8.     {
    9.       Debug.Log(a); // recognized at this point as a defined class field (defined means having a value)
    10.       Debug.Log(b); // defined in loop initializer
    11.     }
    12.     Debug.Log(a); // still works
    13.     Debug.Log(b); // doesn't work
    14.   }
    15. }
    Be mindful of naming conventions. This is just an example, but private class fields are typically prefixed with an underscore or m_ (in this case either _a or m_a; m is short for 'member', as in class member) to make the fact that it's not locally declared more apparent, because it won't be discarded after the function call is over. Fields are commonly used to introduce so-called side-effects into complex behaviors and to provide backing fields for various instance properties that stay persistent for as long as the object is alive in memory.
     
    Last edited: Oct 3, 2020
    Unscriptableeee likes this.
  8. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    btw, it's worth noting, if you declare a variable inside a loop block, it doesn't incur any performance penalties (this isn't the case in all languages, especially some high-level scripting languages)
    Code (csharp):
    1. for(int c = 0; c < 17; c++) {
    2.   int x; // this won't be executed every time, compiler knows what you mean
    3.   x = 20 - c; // this will be executed every time, as expected
    4.   Debug.Log(x);
    5. }
    also this form
    int x = 5 + a;
    is just a compound version of two separate concepts
    Code (csharp):
    1. int x; // this is declaration (identifier 'x' holds values of type 'int')
    2. x = 5 + a; // this is definition
    3. // expression-wise it's an assignment preceded by addition,
    4. // but a definition is when a valueless declared variable gets its value for the first time
    compiler will yell at you if you attempt to use an undefined variable, therefore you cannot let variables stay undefined during run-time one way or the other.

    this is why the compound thing makes sense most of the time, but sometimes you need to do this
    Code (csharp):
    1. int x; // I'm not sure what I will become
    2.  
    3. if(something > 0) {
    4.   x = -1;
    5. } else {
    6.   x = something * 5;
    7.   // x++; // this can't work because it was undefined
    8. }
    etc.
     
  9. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    oh one other thing, maybe helpful, as it's somewhat related to how variables are treated in C#, if you do compound declaration/definition then you can use 'var' keyword, as seen in higher scripting languages.

    for example
    int x = 5 + a;


    can also be written as
    var x = 5 + a;


    because the compiler is able to infer the actual type from the expression alone.

    this is especially useful if you have variables that have really long and repeating declarations, such as
    Code (csharp):
    1. Dictionary<int, MyPayloadType> myDictionary = (Dictionary<int, MyPayloadType>)MyDictionaryFactory.BuildFromArray(myArray);
    with the use of generics and type inference this can look as innocuous as

    Code (csharp):
    1. var myDictionary = MyDictionaryFactory.BuildFromArray(myArray);
    Explanation:
    because the compiler can now look at the type of the array elements, and observe that the generic method BuildFromArray<T> expects T[] and that its return type is Dictionary<int, T>, it can infer that when myArray is of type MyPayloadType[], the method is to be called as
    MyDictionaryFactory.BuildFromArray<MyPayloadType>(MyPayloadType[])
    and the result has to be Dictionary<int, MyPayloadType> without specifying anything, thus var will declare myDictionary as intended.

    however, this doesn't work for declarations only
    Code (csharp):
    1. var x; // can't tell what this is supposed to be
    or for multiple declarations like
    Code (csharp):
    1. var somePoint, someOtherPoint = Vector3.zero; // won't work
     
  10. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,590
    Actually b would contain 6. You pass 5 into your function, the function uses this value (internally known as 'a') and does stuff with it. In your case you increment it. You then later return some value (in this case also 'a', but doesnt have to be), which is then saved in the 'b' i defined in the previous example.
    Maybe it makes more sense to think about it like functions in math. Some int y = f(x), so you have some input x, put it into a function, and get the resulting y.

    Orion gave a lot of useful examples, but the main term you may want to look into is "Scope". Scopes are basically these curley backets {} we use to define the areas of classes, methods, statements and so on. Within these scopes we can define variables. And a variable is only known if it's contained inside the scope you are currently in, or a parenting scope. So a method has access to its own variables, as well as the variables defined in its own class, but not variables defined in a different method. It's about visibility. A bit off-topic, but for visibility between different classes we have the private, public, protected, .. keywords, in case you ever wondered what that's about.
     
  11. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,531
    No, it would contain 5 because he used the post increment operator a++ and not the pre increment operator ++a. The difference between the two operators is when the expression value is evaluated. In the post increment operator the value is first evaluated, then the variable is incremented. Think of it like this:

    return a++;
    is essentially equal to

    Code (CSharp):
    1. int tmp = a;
    2. a = a + 1;
    3. return tmp;
    However using the pre increment
    return ++a;
    it's just like

    Code (CSharp):
    1.  
    2. a = a + 1;
    3. return a;
    So in his case, since "a" is a local variable post incrementing in a return statement is completely pointless.
     
  12. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    True. A value is returned before it's incremented. Now that's just silly :)

    @OP why not simply
    return a + 1;
    ? It's completely meaningless the way it is.
    Refrain from doing fancy operations until you get a good grip what's going on with them.

    We're using ++ and -- when we want to store the changed value back.
    And it matters whether it comes before or after a variable. As Bunny83 said, there is a prefix variant along with the suffix one. Obviously, because we want to store the changed value back, you can't use any of these on the expression, because they apply only to variables.

    However, even though you've managed to change the original variable a, you've failed to notice two things:
    1) As Bunny83 observed, the change was practically ignored, because it comes after return.
    2) Even if you'd change this to pre-increment, the change would be ignored, because a is declared as a primitive value, and these are passed by value.

    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class MyClass : MonoBehaviour {
    4.  
    5.   void Start() {
    6.     int a = 5;
    7.     MyIncrMethod(a);
    8.     Debug.Log(a); // 5
    9.   }
    10.  
    11.   int MyIncrMethod(int a) {
    12.     a++;
    13.   }
    14.  
    15. }
    If we add return, obviously we can capture the value and get it back, but that doesn't change the fact that a stays the same.
    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class MyClass : MonoBehaviour {
    4.  
    5.   void Start() {
    6.     int w = 5;
    7.     w = MyIncrMethod(w);
    8.     Debug.Log(w); // 6
    9.   }
    10.  
    11.   int MyIncrMethod(int a) { // the value here is just a copy, because the argument is passed by value
    12.     a++; // this operation doesn't work on the original location in memory
    13.     return a; // we now capture locally incremented 'a' and return it
    14.   }
    15.  
    16.   // this will also work
    17.   // int MyIncrMethod(int a) {
    18.   //   return ++a;
    19.   // }
    20.  
    21. }
    we could've just as well used return a + 1 for the same result
    (this is, in fact, recommended in this case, why assigning something back if it's going to be wasted?)
    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class MyClass : MonoBehaviour {
    4.  
    5.   void Start() {
    6.     int w = 5; // notice the different names between two contexts?
    7.     w = MyIncrMethod(w); // any relationship ends here
    8.     Debug.Log(w); // 6
    9.   }
    10.  
    11.   int MyIncrMethod(int a) {
    12.     return a + 1;
    13.   }
    14.  
    15. }
    There is a way however to change the primitive value from inside the method
    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class MyClass : MonoBehaviour {
    4.  
    5.   void Start() {
    6.     int w = 5;
    7.     MyIncrMethod(ref w); // the argument is now passed by reference
    8.     Debug.Log(w); // 6
    9.   }
    10.  
    11.   void MyIncrMethod(ref int a) {
    12.     a++;
    13.   }
    14.  
    15. }
     
    Last edited: Oct 4, 2020
    Bunny83 likes this.
  13. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,590
    Uff i overlooked that, good thing you caught and corrected it :)
     
  14. Unscriptableeee

    Unscriptableeee

    Joined:
    Apr 14, 2020
    Posts:
    12
    It was for learning purposes, this is not a code used in a real game.
     
  15. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Because you totally wrote it much better in that other code right? ;)
    Of course, nobody here has any doubts that you can do better.

    As long as you keep learning, you're fine.