-
Notifications
You must be signed in to change notification settings - Fork 7.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Restrict use of [ref] to variables #6807
Comments
It does exactly what you would expect from other languages: it creates a durable reference to a specific instance. The SO item in question was using |
Thanks, @BrucePay, but this issue is not about how
What you're demonstrating is not per se about data structures (that aspect is incidental), you're demonstrating use with a variable, i.e., effectively creating a variable alias (this is also covered in my answer to the SO question).
Yes, it would have worked - but it also would have been pointless. Pointing to a piece of data (a) only makes sense with instances of reference types and (b) you can use a regular variable to do that - using Thus, my point was that using it with a [parameter] variable is the only use that makes sense Or am I missing other legitimate uses of |
Since I'm the OP of the item in question, I thought I'd provide a little more insight into what I was trying to do and how I got to the bad
I hope this code explains why I was trying to point to an array element instead of just using the parent variable containing the array. Second, I did understand that there might be an issue with array concatenation, that is why I first attempted to test with a simple So, now I know that creating references to a piece of data has no practical uses, and I should be fine using |
@the-CPU1: Thanks for the explanation - what you did is an understandable thing to try, especially given that the language doesn't prevent you from doing so. So, if there's consensus that applying |
Bad example - how about this :-)
or this
You get a persistent pointer to the data stored in in the array member. Getting a pointer to a specific location in memory is not supported in PowerShell.
Everything in PowerShell is already a pointer (object reference) so the set of circumstances where |
That's an example of pointless use of $r = [system.collections.generic.list[object]]::new()
$r.Add(1)
$r.Add(2)
$r # prints the list Unless I'm missing something, there is no good reason to use
The [ref] $rerr = ([System.Collections.ObjectModel.Collection[System.Management.Automation.PSParseError]]::new()) Or, to localize the by-reference passing: # Declare as regular variable.
$err = ([System.Collections.ObjectModel.Collection[System.Management.Automation.PSParseError]]::new())
# Pass with ad-hoc [ref] cast
$null = [system.management.automation.psparser]::Tokenize("2 2 2", [ref] $err)
# $err - still a regular variable - was assigned a value in the method call.
$err Note that this idiom is also the form found in the v3.0 language spec. |
This is what got me confused initially. Suppose we have a function:
I was trying to see if something like this would work:
And it did. But then this call didn't work:
And this doesn't work either:
I thought that if first would work, so would the second one, and vice versa. I do understand why it didn't work in the second call - that's the way arrays and If I were to make a guess, I'd think that the first call was specifically coded for by the developers (aka aliases), since there might be a need for a user to pass an array to a function and then modify the size of that array. I'd also guess that the second call behaves "normally", as one would expect if there were no aliases. |
I think you misunderstand the way the |
@rkeithhill correctly points out that you have a misconception about However, even if we construct the nested array the way you intended - i.e., using $c = , , 0; foo ([ref] $c[0]); $c[0] The problem here is again that $c = , , 0; $d = $c[0]; foo ([ref] $d); $c[0] This is basically the same scenario above, except that the by-reference passing works for the intermediate variable
Note that As @BrucePay states, you need And while you can use Just to be clear: As an aside: While maintaining reference equality is rarely a concern in PowerShell, this cloning is problematic from a performance perspective. |
An attempt to summarize and clear (at least my) conceptual fog (arrived at without source-code analysis; do let me know if and where I'm wrong):
Why Note: There is one edge case: When you use
Therefore, my preference is to disallow
|
Re improving the documentation: please see MicrosoftDocs/PowerShell-Docs#2402. |
That is what I remember from my C days. In my first example above, inside the callee
One more question on this:
I understand that I can simply reference |
Indeed: I understand the intent behind $a = @{ Children = New-Object System.Collections.ArrayList }
$b = [ref] $a.Children
$b.Value.Add(1) but the point is that the use of $a = @{ Children = New-Object System.Collections.ArrayList }
$b = $a.Children # No need for [ref] - obtain a reference to the array list
$b.Add(1) # Operate on the array list directly. Again, note that this only works because the value of the |
That last example explains a lot. There really isn't a good reason to use |
I sometimes use $innerVar = [ref] 0
& { $innerVar.Value = 10 }
$innerVar.Value
# 10 You could use a bunch of other things here like |
That's a good example in principle, but note that it isn't about value types - it's about (conveniently) modifying a variable in a parent scope. Without the scoping issue involved, again a simple variable will do - note the use of $var = 0
. { $var = 10 }
$var # 10 Because Implementing the same thing without $var = 0
& { (Get-Variable var -Scope 1).Value = 10 }
$var # 10 So, as long as |
Well, yes and no. This is just semantics but you aren't modifying the variable. The variable in the child scope is a different variable but it holds a reference to the same object or the value of a value type. I mentioned value types because if the variable instead held a reference type you could adjust it as you would in the parent scope (with the exception of replacing it entirely) But with a value type you need to either change the value of the variable from the previous scope (like your example) or place it into a reference type. More specifically I'd say it's useful for creating an explicit reference to an object. |
I see what you're saying and "it's useful for creating an explicit reference to an object" is a good summary. As the whole discussion here shows, users need guidance with respect to the primary purpose of This guidance is missing from the documentation, so let's try to summarize in preparation for updating it:
Does that sound correct and comprehensive to you? Your example inspired me to rethink a scenario in which I did use If you use script-block parameter values, such as for calculating the value of the I solved that problem with $i = 0; $iVar = Get-Variable -Name i
Get-ChildItem -File $setPath | Rename-Item -NewName { ... $iVar.Value++ ... } But your technique enables a more elegant solution: $iRef = [ref] 0
Get-ChildItem -File $setPath | Rename-Item -NewName { ... $iRef.Value++ ... } |
Yes that is an excellent summary 👍 |
Thanks, @SeeminglyScience. I've transferred the relevant information to MicrosoftDocs/PowerShell-Docs#2402, so we can close this. |
, (
([REF] 0, [REF] 1), ([REF] 1, [REF] 0) |
% { , ($_, { $_ | SORT -T:1 | % VALUE }) } |
% { , ($_[0]) | % $_[1] }
) |
% { $_[0] | SHOULD -BE $_[1] } Explanation for hoomans (in case any come around):
Is this a problem with , (
([REF] 0, [REF] 1), ([REF] 1, [REF] 0) | % { , ($_, { $_ | SORT VALUE -T:1 | % VALUE }) } | % { , ($_[0]) | % $_[1] }
) |
% { $_[0] | SHOULD -BE $_[1] } The workaround means to explicitly sort by value. |
@yecril71pl ref just isn't sortable. It's probably doing |
For the ultimate resolution, see MicrosoftDocs/PowerShell-Docs#2402
From what I understand, use of
[ref]
only makes sense when applied to a variable or parameter [variable].Assuming this assumption holds, perhaps nonsensical uses such as
[ref] 'foo'
or[ref] $hashtable.key1
could be flagged as syntax errors.The confusion that not preventing such pointless uses can create is exemplified by this SO question, in which the OP thought they could create a persistent reference to a specific hashtable entry as follows (simplified):
Environment data
Written as of:
PowerShell Core v6.0.2
The text was updated successfully, but these errors were encountered: