Thanks, everyone, for the great reprexes, and welcome to RStudio Community!
It turns out that the real issue is a quirk of base R. read.csv() returns a regular data frame, and when we try to subset a single column in a data frame, R converts the object to a vector:
x <- data.frame(a = 1:5)
x
#> a
#> 1 1
#> 2 2
#> 3 3
#> 4 4
#> 5 5
x[, "a"]
#> [1] 1 2 3 4 5
is.data.frame(x[, "a"])
#> [1] FALSE
ggplot() expects a data frame, not a vector. When you're dealing with base R, the solution is to set drop = FALSE, which keeps x as a data frame.
x[, "a", drop = FALSE]
#> a
#> 1 1
#> 2 2
#> 3 3
#> 4 4
#> 5 5
is.data.frame(x[, "a", drop = FALSE])
#> [1] TRUE
Using this approach will fix our issue (using @Deirdre_Belson and crew's example):
library(ggplot2)
just_speed <- cars[, "speed", drop = FALSE]
ggplot(just_speed, aes(x = speed)) +
geom_histogram()
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Notably, the original code used read.csv(), which returns a regular data frame, but readr::read_csv() returns a tibble, a special case of the data frame. Tibbles don't have this behavior, and subsetting them always returns a tibble:
library(tidyverse)
y <- tibble(a = 1:5)
y[, "a"]
#> # A tibble: 5 x 1
#> a
#> <int>
#> 1 1
#> 2 2
#> 3 3
#> 4 4
#> 5 5
Using a tibble also solves our problem:
library(tidyverse)
cars <- as_tibble(cars)
just_speed <- cars[, "speed"]
ggplot(just_speed, aes(x = speed)) +
geom_histogram()
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Interestingly, @ajacoby and crew posed a different but equally valid question: can we make histograms out of integer vectors? ggplot2's quickplot() function does this by default:
library(ggplot2)
vals <- 1:50
quickplot(vals)
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Of course, we can always wrap vals in a data frame (e.g. data.frame(vals = vals) or tibble(vals = vals)), then call ggplot() as normal.
Great work, everyone!