Because you're actually storing a dataframe into why - not a vector.
> str(df)
'data.frame': 5 obs. of 4 variables:
$ x : int 1 2 3 4 5
$ y : int 1 2 3 4 5
$ z : chr "a" "a" "a" "a" ...
$ why:'data.frame': 5 obs. of 1 variable:
..$ x: int 1 4 9 16 25
> str(df[1]*df[2])
'data.frame': 5 obs. of 1 variable:
$ x: int 1 4 9 16 25
> str(df[,1] * df[,2])
int [1:5] 1 4 9 16 25
> df$why2 <- df[,1]*df[,2]
> df
x y z x why2
1 1 1 a 1 1
2 2 2 a 4 4
3 3 3 a 9 9
4 4 4 a 16 16
5 5 5 a 25 25
df[1] returns the first element of df as a sublist. A dataframe is a special type of list which is why you can use this type of indexing to grab columns. However only using the single bracket tells it to return a sublist containing the element of interest (instead of just the element of interest).